Compare commits
162 Commits
feature/dr
...
v5.0.0-bet
Author | SHA1 | Date | |
---|---|---|---|
![]() |
807a75a265 | ||
![]() |
6e1b44ea56 | ||
![]() |
d870a54f58 | ||
![]() |
9f464a3f23 | ||
![]() |
ed4e84d665 | ||
![]() |
1e4ab96233 | ||
![]() |
380c034ff5 | ||
![]() |
7c9abe432d | ||
![]() |
2ca853c22c | ||
![]() |
b23d55612a | ||
![]() |
b261faacfc | ||
![]() |
9984b32876 | ||
![]() |
d63ba75430 | ||
![]() |
2db903ed94 | ||
![]() |
3f9bc4d335 | ||
![]() |
3df95d0e21 | ||
![]() |
f445695f6a | ||
![]() |
6f3163d7bd | ||
![]() |
5421eae23d | ||
![]() |
a0e82ee22f | ||
![]() |
1fbf822769 | ||
![]() |
6b0f037722 | ||
![]() |
ae231d2fe6 | ||
![]() |
2e95899da7 | ||
![]() |
3f45b38dfc | ||
![]() |
daf4d36348 | ||
![]() |
477cd16f23 | ||
![]() |
83eded0056 | ||
![]() |
338de9c149 | ||
![]() |
6d99d4e661 | ||
![]() |
4e5a8060c8 | ||
![]() |
eb938e39ff | ||
![]() |
cff8604a5f | ||
![]() |
2afa54bcd2 | ||
![]() |
4da6025b6f | ||
![]() |
c4f64444e7 | ||
![]() |
b0205b296f | ||
![]() |
e0c8b3cc81 | ||
![]() |
80ca92336e | ||
![]() |
6c0681e4ca | ||
![]() |
26e8503dc8 | ||
![]() |
c1ce78efde | ||
![]() |
2f52c2f062 | ||
![]() |
24d11a2446 | ||
![]() |
e1d4ae5bbd | ||
![]() |
dcfe346f54 | ||
![]() |
3906db9efe | ||
![]() |
ba5244d95d | ||
![]() |
52db730870 | ||
![]() |
82d2278ebd | ||
![]() |
30d08ac72d | ||
![]() |
6f1e71c8e5 | ||
![]() |
385be9b5f5 | ||
![]() |
25cb041d78 | ||
![]() |
18fa9cd659 | ||
![]() |
ce760541c5 | ||
![]() |
db51b652f0 | ||
![]() |
4222d98aa9 | ||
![]() |
484f1e5dcb | ||
![]() |
7304b94a77 | ||
![]() |
ee5ec7df72 | ||
![]() |
9a1e4bdf7c | ||
![]() |
a43cca7629 | ||
![]() |
0cdabab2ec | ||
![]() |
28b968ea99 | ||
![]() |
5160033092 | ||
![]() |
ce086c07b9 | ||
![]() |
bf9348488d | ||
![]() |
70b3e0701b | ||
![]() |
dc3d890937 | ||
![]() |
f68be32e2d | ||
![]() |
3758e7af03 | ||
![]() |
5ab1858349 | ||
![]() |
54353bcb24 | ||
![]() |
de7640558d | ||
![]() |
2e17b16516 | ||
![]() |
345f9f1786 | ||
![]() |
8c9214ce5e | ||
![]() |
fc9cc8046d | ||
![]() |
e592bb855d | ||
![]() |
f8407264da | ||
![]() |
202fe2b69a | ||
![]() |
d14a93f9d0 | ||
![]() |
1d1ccdbf06 | ||
![]() |
92fd647316 | ||
![]() |
d8ca506200 | ||
![]() |
928d6eda98 | ||
![]() |
d5e6755694 | ||
![]() |
a1431126ec | ||
![]() |
13c8caeb8c | ||
![]() |
b2b5ee0e19 | ||
![]() |
ff74ecd7c9 | ||
![]() |
0ac3e79156 | ||
![]() |
3a69936e2e | ||
![]() |
e738d5dd5f | ||
![]() |
ed8ee21c04 | ||
![]() |
87220a941a | ||
![]() |
4425d13c7a | ||
![]() |
b818e5f493 | ||
![]() |
403563ba32 | ||
![]() |
156ca68116 | ||
![]() |
de3677a6c3 | ||
![]() |
3b76dfe9f4 | ||
![]() |
6e38ee6d16 | ||
![]() |
454657b00f | ||
![]() |
aad0c4e909 | ||
![]() |
d106ee9ac1 | ||
![]() |
5b1e6e4381 | ||
![]() |
e1106c6145 | ||
![]() |
0c5caf9986 | ||
![]() |
2fdcd6c639 | ||
![]() |
8332adf07b | ||
![]() |
fb40e908c0 | ||
![]() |
b6ef67568a | ||
![]() |
9c591ec3b1 | ||
![]() |
495900c083 | ||
![]() |
7874e2af27 | ||
![]() |
19b37b7111 | ||
![]() |
8593249bc6 | ||
![]() |
6d483ec887 | ||
![]() |
e1a28f0f90 | ||
![]() |
c2ba76a821 | ||
![]() |
c4f8134d89 | ||
![]() |
59416fd0cd | ||
![]() |
ef7ed24114 | ||
![]() |
07dd2bcb5c | ||
![]() |
81dfeb3ab1 | ||
![]() |
09d558744f | ||
![]() |
abe7dd7ccd | ||
![]() |
0da624dc18 | ||
![]() |
a082585b4e | ||
![]() |
d1582723ba | ||
![]() |
829a88f290 | ||
![]() |
79f2e57fa9 | ||
![]() |
322787ba0e | ||
![]() |
7bd2dab47c | ||
![]() |
dd1446cfb0 | ||
![]() |
cf96db75ea | ||
![]() |
da4f350cdb | ||
![]() |
862c012bb6 | ||
![]() |
72a65cb1d2 | ||
![]() |
22379ae31b | ||
![]() |
73e0ba8264 | ||
![]() |
2b0e28f456 | ||
![]() |
151a9b1a74 | ||
![]() |
172d7f049e | ||
![]() |
5ef703eff4 | ||
![]() |
98d970de30 | ||
![]() |
8896fb280a | ||
![]() |
8df5ab9e62 | ||
![]() |
eb32d51999 | ||
![]() |
8901809f88 | ||
![]() |
1de22ef53b | ||
![]() |
d328ae0c7e | ||
![]() |
dabb0f9dad | ||
![]() |
5adcfc5bad | ||
![]() |
b03bf86a35 | ||
![]() |
db1b1eadf1 | ||
![]() |
939266b18b | ||
![]() |
b68a9d8b47 | ||
![]() |
af1a028203 | ||
![]() |
c697d5fb99 |
7
.vscode/settings.json
vendored
@@ -62,7 +62,12 @@
|
||||
"__tuple": "cpp",
|
||||
"hash_map": "cpp",
|
||||
"hash_set": "cpp",
|
||||
"system_error": "cpp"
|
||||
"system_error": "cpp",
|
||||
"__nullptr": "cpp",
|
||||
"__functional_base": "cpp",
|
||||
"__functional_base_03": "cpp",
|
||||
"chrono": "cpp",
|
||||
"ratio": "cpp"
|
||||
},
|
||||
"files.exclude": {
|
||||
"Binaries/*build*": true,
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.8 KiB |
BIN
Binaries/Output/Release_Windows/res/GD-logo-big.png
Normal file
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 324 B |
After Width: | Height: | Size: 382 B |
@@ -500,7 +500,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(const gd::String & parame
|
||||
gd::ExpressionParser parser(parameter);
|
||||
if ( !parser.ParseMathExpression(platform, project, scene, callbacks) )
|
||||
{
|
||||
cout << "Error :" << parser.firstErrorStr << " in: "<< parameter << endl;
|
||||
cout << "Error :" << parser.GetFirstError() << " in: "<< parameter << endl;
|
||||
|
||||
argOutput = "0";
|
||||
}
|
||||
@@ -514,7 +514,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(const gd::String & parame
|
||||
gd::ExpressionParser parser(parameter);
|
||||
if ( !parser.ParseStringExpression(platform, project, scene, callbacks) )
|
||||
{
|
||||
cout << "Error in text expression" << parser.firstErrorStr << endl;
|
||||
cout << "Error in text expression" << parser.GetFirstError() << endl;
|
||||
|
||||
argOutput = "\"\"";
|
||||
}
|
||||
|
@@ -169,8 +169,8 @@ bool CallbacksForGeneratingExpressionCode::OnSubMathExpression(const gd::Platfor
|
||||
if ( !parser.ParseMathExpression(platform, project, layout, callbacks) )
|
||||
{
|
||||
#if defined(GD_IDE_ONLY)
|
||||
firstErrorStr = callbacks.firstErrorStr;
|
||||
firstErrorPos = callbacks.firstErrorPos;
|
||||
firstErrorStr = callbacks.GetFirstError();
|
||||
firstErrorPos = callbacks.GetFirstErrorPosition();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
@@ -188,8 +188,8 @@ bool CallbacksForGeneratingExpressionCode::OnSubTextExpression(const gd::Platfor
|
||||
if ( !parser.ParseStringExpression(platform, project, layout, callbacks) )
|
||||
{
|
||||
#if defined(GD_IDE_ONLY)
|
||||
firstErrorStr = callbacks.firstErrorStr;
|
||||
firstErrorPos = callbacks.firstErrorPos;
|
||||
firstErrorStr = callbacks.GetFirstError();
|
||||
firstErrorPos = callbacks.GetFirstErrorPosition();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
@@ -31,21 +31,31 @@ public:
|
||||
virtual ~ExpressionParser() {};
|
||||
|
||||
/**
|
||||
* Parse the expression, calling each functor when necessary
|
||||
* \brief Parse the expression, calling each functor when necessary
|
||||
* \return True if expression was correctly parsed.
|
||||
*/
|
||||
bool ParseMathExpression(const gd::Platform & platform, const gd::Project & project, const gd::Layout & layout, gd::ParserCallbacks & callbacks);
|
||||
|
||||
/**
|
||||
* Parse the expression, calling each functor when necessary
|
||||
* \brief Parse the expression, calling each functor when necessary
|
||||
* \return True if expression was correctly parsed.
|
||||
*/
|
||||
bool ParseStringExpression(const gd::Platform & platform, const gd::Project & project, const gd::Layout & layout, gd::ParserCallbacks & callbacks);
|
||||
|
||||
gd::String firstErrorStr;
|
||||
size_t firstErrorPos;
|
||||
/**
|
||||
* \brief Return the description of the error that was found
|
||||
*/
|
||||
const gd::String & GetFirstError() { return firstErrorStr; }
|
||||
|
||||
/**
|
||||
* \brief Return the position of the error that was found
|
||||
* \return The position, or gd::String::npos if no error is found
|
||||
*/
|
||||
size_t GetFirstErrorPosition() { return firstErrorPos; }
|
||||
|
||||
private:
|
||||
gd::String firstErrorStr;
|
||||
size_t firstErrorPos;
|
||||
|
||||
/**
|
||||
* Tool function to add a parameter
|
||||
@@ -103,8 +113,21 @@ public:
|
||||
virtual bool OnSubMathExpression(const gd::Platform & platform, const gd::Project & project, const gd::Layout & layout, gd::Expression & expression) = 0;
|
||||
virtual bool OnSubTextExpression(const gd::Platform & platform, const gd::Project & project, const gd::Layout & layout, gd::Expression & expression) = 0;
|
||||
|
||||
/**
|
||||
* \brief Return the description of the error that was found
|
||||
*/
|
||||
const gd::String & GetFirstError() { return firstErrorStr; }
|
||||
|
||||
/**
|
||||
* \brief Return the position of the error that was found
|
||||
* \return The position, or gd::String::npos if no error is found
|
||||
*/
|
||||
size_t GetFirstErrorPosition() { return firstErrorPos; }
|
||||
|
||||
protected:
|
||||
gd::String firstErrorStr;
|
||||
size_t firstErrorPos;
|
||||
|
||||
private:
|
||||
/**
|
||||
* \brief Set the return type of the expression: Done by ExpressionParser according to
|
||||
|
@@ -30,7 +30,7 @@ namespace gd
|
||||
|
||||
gd::VariableParser parser(parameter);
|
||||
if ( !parser.Parse(callbacks) )
|
||||
cout << "Error :" << parser.firstErrorStr << " in: "<< parameter << endl;
|
||||
cout << "Error :" << parser.GetFirstError() << " in: "<< parameter << endl;
|
||||
\endcode
|
||||
*
|
||||
* Here is the parsed grammar: <br>
|
||||
@@ -61,6 +61,17 @@ public:
|
||||
*/
|
||||
bool Parse(VariableParserCallbacks & callbacks);
|
||||
|
||||
/**
|
||||
* \brief Return the description of the error that was found
|
||||
*/
|
||||
const gd::String & GetFirstError() { return firstErrorStr; }
|
||||
|
||||
/**
|
||||
* \brief Return the position of the error that was found
|
||||
* \return The position, or gd::String::npos if no error is found
|
||||
*/
|
||||
size_t GetFirstErrorPosition() { return firstErrorPos; }
|
||||
|
||||
gd::String firstErrorStr;
|
||||
size_t firstErrorPos;
|
||||
|
||||
|
@@ -621,6 +621,18 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(gd:
|
||||
.AddParameter("objectList", _("Objects"))
|
||||
.MarkAsSimple();
|
||||
|
||||
obj.AddCondition("CollisionPoint",
|
||||
_("Point inside object"),
|
||||
_("Test if a point is inside the object collision masks."),
|
||||
_("_PARAM1_;_PARAM2_ is inside _PARAM0_"),
|
||||
_("Collision"),
|
||||
"res/conditions/collisionPoint24.png",
|
||||
"res/conditions/collisionPoint.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("expression", _("X position of the point"))
|
||||
.AddParameter("expression", _("Y position of the point"))
|
||||
.MarkAsSimple();
|
||||
|
||||
obj.AddExpression("X", _("X position"), _("X position of the object"), _("Position"), "res/actions/position.png")
|
||||
.AddParameter("object", _("Object"));
|
||||
|
||||
|
@@ -131,7 +131,7 @@ public:
|
||||
* \warning If the image has not been loaded (using LoadImage) and the center point is set as automatic,
|
||||
* the returned point won't be correct.
|
||||
*/
|
||||
inline Point & GetCenter() { automaticCentre = false; return centre; }
|
||||
inline Point & GetCenter() { return centre; }
|
||||
|
||||
/**
|
||||
* \brief Return true if the center point is automatically computed.
|
||||
|
@@ -477,8 +477,8 @@ void EditExpressionDialog::TextModified(wxStyledTextEvent& event)
|
||||
gd::ExpressionParser expressionParser(expression);
|
||||
if ( !expressionParser.ParseMathExpression(project.GetCurrentPlatform(), project, layout, callbacks) )
|
||||
{
|
||||
errorTxt->SetLabel(expressionParser.firstErrorStr);
|
||||
lastErrorPos = expressionParser.firstErrorPos;
|
||||
errorTxt->SetLabel(expressionParser.GetFirstError());
|
||||
lastErrorPos = expressionParser.GetFirstErrorPosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -516,8 +516,8 @@ void EditStrExpressionDialog::TextModified(wxStyledTextEvent& event)
|
||||
gd::ExpressionParser expressionParser(text);
|
||||
if ( !expressionParser.ParseStringExpression(project.GetCurrentPlatform(), project, layout, callbacks) )
|
||||
{
|
||||
errorTxt->SetLabel(expressionParser.firstErrorStr);
|
||||
lastErrorPos = expressionParser.firstErrorPos;
|
||||
errorTxt->SetLabel(expressionParser.GetFirstError());
|
||||
lastErrorPos = expressionParser.GetFirstErrorPosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -28,8 +28,8 @@ bool CallbacksForExpressionCorrectnessTesting::OnSubMathExpression(const gd::Pla
|
||||
if ( !parser.ParseMathExpression(platform, project, layout, callbacks) )
|
||||
{
|
||||
#if defined(GD_IDE_ONLY)
|
||||
firstErrorStr = callbacks.firstErrorStr;
|
||||
firstErrorPos = callbacks.firstErrorPos;
|
||||
firstErrorStr = callbacks.GetFirstError();
|
||||
firstErrorPos = callbacks.GetFirstErrorPosition();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
@@ -45,8 +45,8 @@ bool CallbacksForExpressionCorrectnessTesting::OnSubTextExpression(const gd::Pla
|
||||
if ( !parser.ParseStringExpression(platform, project, layout, callbacks) )
|
||||
{
|
||||
#if defined(GD_IDE_ONLY)
|
||||
firstErrorStr = callbacks.firstErrorStr;
|
||||
firstErrorPos = callbacks.firstErrorPos;
|
||||
firstErrorStr = callbacks.GetFirstError();
|
||||
firstErrorPos = callbacks.GetFirstErrorPosition();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
@@ -143,13 +143,13 @@ void InstructionSentenceFormatter::LoadTypesFormattingFromConfig()
|
||||
{
|
||||
//Load default configuration
|
||||
typesFormatting.clear();
|
||||
typesFormatting["expression"].SetColor(99, 0, 0).SetBold();
|
||||
typesFormatting["object"].SetColor(19, 81, 0).SetBold();
|
||||
typesFormatting["behavior"].SetColor(19, 81, 0).SetBold();
|
||||
typesFormatting["operator"].SetColor(64, 81, 79).SetBold();
|
||||
typesFormatting["objectvar"].SetColor(44, 69, 99).SetBold();
|
||||
typesFormatting["scenevar"].SetColor(44, 69, 99).SetBold();
|
||||
typesFormatting["globalvar"].SetColor(44, 69, 99).SetBold();
|
||||
typesFormatting["expression"].SetColor(27, 143, 1).SetBold();
|
||||
typesFormatting["object"].SetColor(182, 97, 10).SetBold();
|
||||
typesFormatting["behavior"].SetColor(119, 119, 119).SetBold();
|
||||
typesFormatting["operator"].SetColor(55, 131, 211).SetBold();
|
||||
typesFormatting["objectvar"].SetColor(131, 55, 162).SetBold();
|
||||
typesFormatting["scenevar"].SetColor(131, 55, 162).SetBold();
|
||||
typesFormatting["globalvar"].SetColor(131, 55, 162).SetBold();
|
||||
|
||||
//Load any existing custom configuration
|
||||
#if !defined(GD_NO_WX_GUI)
|
||||
|
@@ -40,6 +40,7 @@ IF (NOT EMSCRIPTEN)
|
||||
ENDIF()
|
||||
ADD_SUBDIRECTORY(PlatformBehavior)
|
||||
ADD_SUBDIRECTORY(PrimitiveDrawing)
|
||||
ADD_SUBDIRECTORY(Shopify)
|
||||
IF (NOT EMSCRIPTEN)
|
||||
ADD_SUBDIRECTORY(SoundObject)
|
||||
ENDIF()
|
||||
|
20
Extensions/Shopify/CMakeLists.txt
Normal file
@@ -0,0 +1,20 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
|
||||
project(Shopify)
|
||||
gd_add_extension_includes()
|
||||
|
||||
#Defines
|
||||
###
|
||||
gd_add_extension_definitions(Shopify)
|
||||
|
||||
#The targets
|
||||
###
|
||||
include_directories(.)
|
||||
file(GLOB source_files *)
|
||||
|
||||
gd_add_extension_target(Shopify "${source_files}" "JsPlatform")
|
||||
|
||||
#Linker files for the IDE extension
|
||||
###
|
||||
gd_extension_link_libraries(Shopify)
|
93
Extensions/Shopify/JsExtension.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
|
||||
GDevelop - Shopify Extension
|
||||
Copyright (c)2017 Florian Rival (Florian.Rival@gmail.com)
|
||||
This project is released under the MIT License.
|
||||
*/
|
||||
#if defined(GD_IDE_ONLY)
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
void DeclareShopifyExtension(gd::PlatformExtension & extension)
|
||||
{
|
||||
extension.SetExtensionInformation("Shopify",
|
||||
_("Shopify"),
|
||||
_("Interact with products and generate URLs for checkouts with your Shopify shop."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)");
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
extension.AddAction("BuildClient",
|
||||
_("Initialize a shop"),
|
||||
_("Initialize a shop with your credentials. Call this action first, and then use the shop name in the other actions to interact with products."),
|
||||
_("Initialize shop _PARAM1_ (domain: _PARAM2_, appId: _PARAM3_)"),
|
||||
_("Shopify"),
|
||||
"JsPlatform/Extensions/Shopifyicon24.png",
|
||||
"JsPlatform/Extensions/Shopifyicon16.png")
|
||||
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("string", _("Shop name"))
|
||||
.AddParameter("string", _("Domain (xxx.myshopify.com)"))
|
||||
.AddParameter("string", _("App Id"))
|
||||
.AddParameter("string", _("Access Token"));
|
||||
|
||||
extension.AddAction("GetCheckoutUrlForProduct",
|
||||
_("Get the URL for buying a product"),
|
||||
_("Get the URL for buying a product from a shop. The URL will be stored in the variable that you specify. You can then use the action to open an URL to redirect the player to the checkout."),
|
||||
_("Get the URL for product #_PARAM2_ (quantity: _PARAM3_, variant: _PARAM4_) from shop _PARAM1_, and store it in _PARAM5_ (or _PARAM6_ in case of error)"),
|
||||
_("Shopify"),
|
||||
"JsPlatform/Extensions/Shopifyicon24.png",
|
||||
"JsPlatform/Extensions/Shopifyicon16.png")
|
||||
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("string", _("Shop name (initialized with \"Initialize a shop\" action)"))
|
||||
.AddParameter("string", _("Product id"))
|
||||
.AddParameter("expression", _("Quantity"))
|
||||
.AddParameter("expression", _("Variant (0 by default)")).SetDefaultValue("0")
|
||||
.AddParameter("scenevar", _("Variable where the URL for checkout must be stored"))
|
||||
.AddParameter("scenevar", _("Variable containing the error (if any)"));
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief This class declares information about the JS extension.
|
||||
*/
|
||||
class ShopifyJsExtension : public gd::PlatformExtension
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Constructor of an extension declares everything the extension contains: objects, actions, conditions and expressions.
|
||||
*/
|
||||
ShopifyJsExtension()
|
||||
{
|
||||
DeclareShopifyExtension(*this);
|
||||
|
||||
GetAllActions()["Shopify::BuildClient"].codeExtraInformation
|
||||
.SetIncludeFile("Extensions/Shopify/shopify-buy.umd.polyfilled.min.js")
|
||||
.AddIncludeFile("Extensions/Shopify/shopifytools.js")
|
||||
.SetFunctionName("gdjs.evtTools.shopify.buildClient");
|
||||
GetAllActions()["Shopify::GetCheckoutUrlForProduct"].codeExtraInformation
|
||||
.SetIncludeFile("Extensions/Shopify/shopify-buy.umd.polyfilled.min.js")
|
||||
.AddIncludeFile("Extensions/Shopify/shopifytools.js")
|
||||
.SetFunctionName("gdjs.evtTools.shopify.getCheckoutUrlForProduct");
|
||||
|
||||
StripUnimplementedInstructionsAndExpressions();
|
||||
GD_COMPLETE_EXTENSION_COMPILATION_INFORMATION();
|
||||
};
|
||||
};
|
||||
|
||||
#if defined(EMSCRIPTEN)
|
||||
extern "C" gd::PlatformExtension * CreateGDJSShopifyExtension() {
|
||||
return new ShopifyJsExtension;
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* Used by GDevelop to create the extension class
|
||||
* -- Do not need to be modified. --
|
||||
*/
|
||||
extern "C" gd::PlatformExtension * GD_EXTENSION_API CreateGDJSExtension() {
|
||||
return new ShopifyJsExtension;
|
||||
}
|
||||
#endif
|
||||
#endif
|
2
Extensions/Shopify/shopify-buy.umd.polyfilled.min.js
vendored
Normal file
63
Extensions/Shopify/shopifytools.js
Normal file
@@ -0,0 +1,63 @@
|
||||
gdjs.ShopifyClientsManager = function() {
|
||||
};
|
||||
|
||||
gdjs.ShopifyClientsManager.set = function(runtimeScene, name, shopifyClient) {
|
||||
var game = runtimeScene.getGame();
|
||||
if (!game.shopifyClients) {
|
||||
game.shopifyClients = {};
|
||||
}
|
||||
|
||||
game.shopifyClients[name] = shopifyClient;
|
||||
}
|
||||
|
||||
gdjs.ShopifyClientsManager.get = function(runtimeScene, name) {
|
||||
var game = runtimeScene.getGame();
|
||||
if (!game.shopifyClients) {
|
||||
game.shopifyClients = {};
|
||||
}
|
||||
|
||||
return game.shopifyClients[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* @namespace gdjs.evtTools
|
||||
* @class shopify
|
||||
* @static
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.shopify = {};
|
||||
|
||||
gdjs.evtTools.shopify.buildClient = function(runtimeScene, name, domain, appId, accessToken) {
|
||||
if (typeof ShopifyBuy === 'undefined') return;
|
||||
|
||||
var config = new ShopifyBuy.Config({
|
||||
accessToken: accessToken,
|
||||
domain: domain,
|
||||
appId: appId
|
||||
});
|
||||
var shopifyClient = ShopifyBuy.buildClient(config);
|
||||
|
||||
gdjs.ShopifyClientsManager.set(runtimeScene, name, shopifyClient);
|
||||
};
|
||||
|
||||
gdjs.evtTools.shopify.getCheckoutUrlForProduct = function(runtimeScene, name,
|
||||
productId, quantity, variantIndex, successVariable, errorVariable) {
|
||||
errorVariable.setString("");
|
||||
successVariable.setString("");
|
||||
|
||||
var shopifyClient = gdjs.ShopifyClientsManager.get(runtimeScene, name);
|
||||
|
||||
shopifyClient.fetchProduct(productId)
|
||||
.then(function (product) {
|
||||
if (variantIndex < 0 || variantIndex >= product.variants.length) {
|
||||
errorVariable.setString("The product has no variant.");
|
||||
return;
|
||||
}
|
||||
|
||||
var variant = product.variants[variantIndex];
|
||||
var checkoutURL = variant.checkoutUrl(quantity);
|
||||
successVariable.setString(checkoutURL);
|
||||
}, function (error) {
|
||||
errorVariable.setString("Unable to get the product that was requested.");
|
||||
});
|
||||
};
|
@@ -29,13 +29,21 @@ gdjs.TextRuntimeObjectPixiRenderer.prototype.ensureUpToDate = function() {
|
||||
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype.updateStyle = function() {
|
||||
var fontName = "\"gdjs_font_" + this._object._fontName + "\"";
|
||||
var style = { align:"left" };
|
||||
style.font = "";
|
||||
if ( this._object._italic ) style.font += "italic ";
|
||||
if ( this._object._bold ) style.font += "bold ";
|
||||
style.font += this._object._characterSize + "px " + fontName;
|
||||
style.fill = "rgb("+this._object._color[0]+","+this._object._color[1]+","+this._object._color[2]+")";
|
||||
this._text.style = style;
|
||||
|
||||
var style = this._text.style;
|
||||
style.fontStyle = this._object._italic ? 'italic' : 'normal';
|
||||
style.fontWeight = this._object._bold ? 'bold' : 'normal';
|
||||
style.fontSize = this._object._characterSize;
|
||||
style.fontFamily = fontName;
|
||||
style.fill = gdjs.rgbToHexNumber(
|
||||
this._object._color[0],
|
||||
this._object._color[1],
|
||||
this._object._color[2]
|
||||
);
|
||||
|
||||
// Manually ask the PIXI object to re-render as we changed a style property
|
||||
// see http://www.html5gamedevs.com/topic/16924-change-text-style-post-render/
|
||||
this._text.dirty = true;
|
||||
};
|
||||
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype.updatePosition = function() {
|
||||
|
@@ -346,7 +346,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(const gd::String & parame
|
||||
gd::VariableParser parser(parameter);
|
||||
if ( !parser.Parse(callbacks) )
|
||||
{
|
||||
cout << "Error :" << parser.firstErrorStr << " in: "<< parameter << endl;
|
||||
cout << "Error :" << parser.GetFirstError() << " in: "<< parameter << endl;
|
||||
argOutput = "runtimeContext->GetSceneVariables().GetBadVariable()";
|
||||
}
|
||||
}
|
||||
@@ -357,7 +357,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(const gd::String & parame
|
||||
gd::VariableParser parser(parameter);
|
||||
if ( !parser.Parse(callbacks) )
|
||||
{
|
||||
cout << "Error :" << parser.firstErrorStr << " in: "<< parameter << endl;
|
||||
cout << "Error :" << parser.GetFirstError() << " in: "<< parameter << endl;
|
||||
argOutput = "runtimeContext->GetGameVariables().GetBadVariable()";
|
||||
}
|
||||
}
|
||||
@@ -373,7 +373,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(const gd::String & parame
|
||||
gd::VariableParser parser(parameter);
|
||||
if ( !parser.Parse(callbacks) )
|
||||
{
|
||||
cout << "Error :" << parser.firstErrorStr << " in: "<< parameter << endl;
|
||||
cout << "Error :" << parser.GetFirstError() << " in: "<< parameter << endl;
|
||||
argOutput = "runtimeContext->GetGameVariables().GetBadVariable()";
|
||||
}
|
||||
}
|
||||
|
@@ -109,7 +109,7 @@ void VariableCodeGenerationCallbacks::OnChildSubscript(gd::String stringExpressi
|
||||
if ( !parser.ParseStringExpression(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetProject(), codeGenerator.GetLayout(), callbacks) )
|
||||
{
|
||||
cout << "Error in text expression" << parser.firstErrorStr << endl;
|
||||
cout << "Error in text expression" << parser.GetFirstError() << endl;
|
||||
argumentCode = "\"\"";
|
||||
}
|
||||
|
||||
|
@@ -69,6 +69,7 @@ BaseObjectExtension::BaseObjectExtension()
|
||||
objectActions["Rebondir"].SetFunctionName("SeparateObjectsWithForces").SetIncludeFile("GDCpp/Extensions/Builtin/ObjectTools.h");
|
||||
objectActions["Ecarter"].SetFunctionName("SeparateObjectsWithoutForces").SetIncludeFile("GDCpp/Extensions/Builtin/ObjectTools.h");
|
||||
objectActions["SeparateFromObjects"].SetFunctionName("SeparateFromObjects").SetIncludeFile("GDCpp/Extensions/Builtin/ObjectTools.h");
|
||||
objectConditions["CollisionPoint"].SetFunctionName("IsCollidingWithPoint");
|
||||
|
||||
|
||||
objectExpressions["X"].SetFunctionName("GetX");
|
||||
|
@@ -128,3 +128,19 @@ CollisionResult GD_API PolygonCollisionTest(Polygon2d & p1, Polygon2d & p2)
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool GD_API IsPointInsidePolygon(Polygon2d & poly, float x, float y)
|
||||
{
|
||||
bool inside = false;
|
||||
sf::Vector2f vi, vj;
|
||||
|
||||
for (std::size_t i = 0, j = poly.vertices.size()-1; i < poly.vertices.size(); j = i++)
|
||||
{
|
||||
vi = poly.vertices[i];
|
||||
vj = poly.vertices[j];
|
||||
if ( ((vi.y>y) != (vj.y>y)) && (x < (vj.x-vi.x) * (y-vi.y) / (vj.y-vi.y) + vi.x) )
|
||||
inside = !inside;
|
||||
}
|
||||
|
||||
return inside;
|
||||
}
|
@@ -33,5 +33,16 @@ struct CollisionResult
|
||||
*/
|
||||
CollisionResult GD_API PolygonCollisionTest(Polygon2d & p1, Polygon2d & p2);
|
||||
|
||||
/**
|
||||
* Check if a point is inside a polygon.
|
||||
*
|
||||
* Uses PNPOLY by W. Randolph Franklin (https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html)
|
||||
*
|
||||
* \return true if the point is inside the polygon
|
||||
*
|
||||
* \ingroup GameEngine
|
||||
*/
|
||||
bool GD_API IsPointInsidePolygon(Polygon2d & poly, float x, float y);
|
||||
|
||||
#endif // POLYGONCOLLISION_H
|
||||
|
||||
|
@@ -379,6 +379,17 @@ bool RuntimeObject::IsCollidingWith(RuntimeObject * obj2)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RuntimeObject::IsCollidingWithPoint(float pointX, float pointY){
|
||||
vector<Polygon2d> hitBoxes = GetHitBoxes();
|
||||
for (std::size_t i = 0; i < hitBoxes.size(); ++i)
|
||||
{
|
||||
if ( IsPointInsidePolygon(hitBoxes[i], pointX, pointY) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void RuntimeObject::SeparateObjectsWithoutForces( std::map <gd::String, std::vector<RuntimeObject*> *> pickedObjectLists)
|
||||
{
|
||||
vector<RuntimeObject*> objects2;
|
||||
|
@@ -223,6 +223,14 @@ public:
|
||||
*/
|
||||
bool IsCollidingWith(RuntimeObject * other);
|
||||
|
||||
/**
|
||||
* \brief Check if a point is inside the object collision hitboxes.
|
||||
* \param pointX The point x coordinate.
|
||||
* \param pointY The point y coordinate.
|
||||
* \return true if the point is inside the object collision hitboxes.
|
||||
*/
|
||||
bool IsCollidingWithPoint(float pointX, float pointY);
|
||||
|
||||
/**
|
||||
* \brief Check collision with each object of the list using their hitboxes, and move the object
|
||||
* according to the sum of the move vector returned by each collision test.
|
||||
|
@@ -103,7 +103,7 @@ ELSE()
|
||||
ELSE()
|
||||
add_custom_target(GDJS_Runtime
|
||||
ALL
|
||||
COMMAND sh "CopyRuntimeToGD.sh" ${GD_base_dir}/Binaries/Output/${CMAKE_BUILD_TYPE}_${CMAKE_SYSTEM_NAME}/JsPlatform/Runtime
|
||||
COMMAND bash "CopyRuntimeToGD.sh" ${GD_base_dir}/Binaries/Output/${CMAKE_BUILD_TYPE}_${CMAKE_SYSTEM_NAME}/JsPlatform/Runtime
|
||||
WORKING_DIRECTORY ${GD_base_dir}/GDJS/scripts)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
@@ -539,7 +539,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(const gd::String & parame
|
||||
gd::VariableParser parser(parameter);
|
||||
if ( !parser.Parse(callbacks) )
|
||||
{
|
||||
cout << "Error :" << parser.firstErrorStr << " in: "<< parameter << endl;
|
||||
cout << "Error :" << parser.GetFirstError() << " in: "<< parameter << endl;
|
||||
argOutput = "gdjs.VariablesContainer.badVariable";
|
||||
}
|
||||
}
|
||||
@@ -550,7 +550,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(const gd::String & parame
|
||||
gd::VariableParser parser(parameter);
|
||||
if ( !parser.Parse(callbacks) )
|
||||
{
|
||||
cout << "Error :" << parser.firstErrorStr << " in: "<< parameter << endl;
|
||||
cout << "Error :" << parser.GetFirstError() << " in: "<< parameter << endl;
|
||||
argOutput = "gdjs.VariablesContainer.badVariable";
|
||||
}
|
||||
}
|
||||
@@ -566,7 +566,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(const gd::String & parame
|
||||
gd::VariableParser parser(parameter);
|
||||
if ( !parser.Parse(callbacks) )
|
||||
{
|
||||
cout << "Error :" << parser.firstErrorStr << " in: "<< parameter << endl;
|
||||
cout << "Error :" << parser.GetFirstError() << " in: "<< parameter << endl;
|
||||
argOutput = "gdjs.VariablesContainer.badVariable";
|
||||
}
|
||||
}
|
||||
|
@@ -110,7 +110,7 @@ void VariableCodeGenerationCallbacks::OnChildSubscript(gd::String stringExpressi
|
||||
if ( !parser.ParseStringExpression(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetProject(), codeGenerator.GetLayout(), callbacks) )
|
||||
{
|
||||
cout << "Error in text expression" << parser.firstErrorStr << endl;
|
||||
cout << "Error in text expression" << parser.GetFirstError() << endl;
|
||||
argumentCode = "\"\"";
|
||||
}
|
||||
|
||||
|
@@ -71,6 +71,7 @@ BaseObjectExtension::BaseObjectExtension()
|
||||
objectConditions["ObjectVariableChildExists"].SetFunctionName("variableChildExists").SetIncludeFile("runtimeobject.js");
|
||||
objectActions["ObjectVariableRemoveChild"].SetFunctionName("variableRemoveChild").SetIncludeFile("runtimeobject.js");
|
||||
objectActions["ObjectVariableClearChildren"].SetFunctionName("variableClearChildren").SetIncludeFile("runtimeobject.js");
|
||||
objectConditions["CollisionPoint"].SetFunctionName("isCollidingWithPoint").SetIncludeFile("runtimeobject.js");
|
||||
|
||||
objectExpressions["X"].SetFunctionName("getX");
|
||||
objectExpressions["Y"].SetFunctionName("getY");
|
||||
|
@@ -136,6 +136,7 @@ gd::PlatformExtension * CreateGDJSTextEntryObjectExtension();
|
||||
gd::PlatformExtension * CreateGDJSInventoryExtension();
|
||||
gd::PlatformExtension * CreateGDJSLinkedObjectsExtension();
|
||||
gd::PlatformExtension * CreateGDJSSystemInfoExtension();
|
||||
gd::PlatformExtension * CreateGDJSShopifyExtension();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -181,6 +182,7 @@ JsPlatform::JsPlatform() :
|
||||
AddExtension(std::shared_ptr<gd::PlatformExtension>(CreateGDJSInventoryExtension())); std::cout.flush();
|
||||
AddExtension(std::shared_ptr<gd::PlatformExtension>(CreateGDJSLinkedObjectsExtension())); std::cout.flush();
|
||||
AddExtension(std::shared_ptr<gd::PlatformExtension>(CreateGDJSSystemInfoExtension())); std::cout.flush();
|
||||
AddExtension(std::shared_ptr<gd::PlatformExtension>(CreateGDJSShopifyExtension())); std::cout.flush();
|
||||
#endif
|
||||
std::cout << "done." << std::endl;
|
||||
};
|
||||
|
@@ -109,7 +109,6 @@ bool Exporter::ExportWholePixiProject(gd::Project & project, gd::String exportDi
|
||||
|
||||
//Prepare the export directory
|
||||
fs.MkDir(exportDir);
|
||||
fs.ClearDir(exportDir);
|
||||
std::vector<gd::String> includesFiles;
|
||||
|
||||
gd::Project exportedProject = project;
|
||||
|
@@ -1,3 +1,24 @@
|
||||
// "Polyfill" self which is not defined in Cocos2d-JS (v3.10)
|
||||
// and used by some library like Shopify SDK.
|
||||
if (typeof self === 'undefined') {
|
||||
console.log("Add polyfill for 'self'");
|
||||
self = window;
|
||||
}
|
||||
|
||||
// Patch XMLHttpRequest.send as Cocos2d-JS v3.15 and below are not supporting
|
||||
// passing null or undefined as parameter, even if it is compliant to do so.
|
||||
if (typeof XMLHttpRequest !== 'undefined') {
|
||||
console.log("Patching XMLHttpRequest for Cocos2d-JS v3.15 and below");
|
||||
var originalSend = XMLHttpRequest.prototype.send;
|
||||
XMLHttpRequest.prototype.send = function(body) {
|
||||
console.log("(Using patched XMLHttpRequest.send)");
|
||||
if (body === null || body === undefined) {
|
||||
return originalSend.call(this);
|
||||
}
|
||||
originalSend.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
cc.game.onStart = function(){
|
||||
if(!cc.sys.isNative && document.getElementById("cocosLoading")) //If referenced loading.js, please remove it
|
||||
document.body.removeChild(document.getElementById("cocosLoading"));
|
||||
|
@@ -100,6 +100,17 @@ gdjs.evtTools.input.keysNameToCode = {
|
||||
"x": 88,
|
||||
"y": 89,
|
||||
"z": 90,
|
||||
|
||||
"Num0": 48,
|
||||
"Num1": 49,
|
||||
"Num2": 50,
|
||||
"Num3": 51,
|
||||
"Num4": 52,
|
||||
"Num5": 53,
|
||||
"Num6": 54,
|
||||
"Num7": 55,
|
||||
"Num8": 56,
|
||||
"Num9": 57,
|
||||
|
||||
"Numpad0": 96,
|
||||
"Numpad1": 97,
|
||||
|
@@ -17,7 +17,7 @@ var gdjs = gdjs || {
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a rgb color value to a hex value.
|
||||
* Convert a rgb color value to a hex string.
|
||||
* @note No "#" or "0x" are added.
|
||||
* @static
|
||||
*/
|
||||
@@ -25,6 +25,14 @@ gdjs.rgbToHex = function(r, g, b) {
|
||||
return "" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a rgb color value to a hex value.
|
||||
* @static
|
||||
*/
|
||||
gdjs.rgbToHexNumber = function(r, g, b) {
|
||||
return (r << 16) + (g << 8) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a random integer between 0 and max.
|
||||
* @method random
|
||||
|
@@ -75,6 +75,8 @@ gdjs.HowlerSoundManager = function(resources)
|
||||
this._resources = resources;
|
||||
this._availableResources = {}; //Map storing "audio" resources for faster access.
|
||||
|
||||
this._globalVolume = 100;
|
||||
|
||||
this._sounds = {};
|
||||
this._musics = {};
|
||||
this._freeSounds = []; //Sounds without an assigned channel.
|
||||
@@ -121,7 +123,7 @@ gdjs.HowlerSoundManager = function(resources)
|
||||
sound.play();
|
||||
}
|
||||
}
|
||||
that._pausedSounds = [];
|
||||
that._pausedSounds.length = 0;
|
||||
that._paused = false;
|
||||
}, false);
|
||||
});
|
||||
@@ -203,7 +205,7 @@ gdjs.HowlerSoundManager.prototype.playSound = function(soundName, loop, volume,
|
||||
gdjs.HowlerSoundManager.prototype.playSoundOnChannel = function(soundName, channel, loop, volume, pitch) {
|
||||
var oldSound = this._sounds[channel];
|
||||
if (oldSound) {
|
||||
oldSound.stop();
|
||||
oldSound.unload();
|
||||
}
|
||||
|
||||
var soundFile = this._getFileFromSoundName(soundName);
|
||||
@@ -244,7 +246,7 @@ gdjs.HowlerSoundManager.prototype.playMusic = function(soundName, loop, volume,
|
||||
gdjs.HowlerSoundManager.prototype.playMusicOnChannel = function(soundName, channel, loop, volume, pitch) {
|
||||
var oldMusic = this._musics[channel];
|
||||
if (oldMusic) {
|
||||
oldMusic.stop();
|
||||
oldMusic.unload();
|
||||
}
|
||||
|
||||
var soundFile = this._getFileFromSoundName(soundName);
|
||||
@@ -268,41 +270,43 @@ gdjs.HowlerSoundManager.prototype.getMusicOnChannel = function(channel) {
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.setGlobalVolume = function(volume) {
|
||||
Howler.volume(volume/100);
|
||||
this._globalVolume = volume;
|
||||
if (this._globalVolume > 100) this._globalVolume = 100;
|
||||
if (this._globalVolume < 0) this._globalVolume = 0;
|
||||
Howler.volume(this._globalVolume/100);
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.getGlobalVolume = function() {
|
||||
return Howler.volume()*100;
|
||||
return this._globalVolume;
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.clearAll = function() {
|
||||
for (var i = 0;i<this._freeSounds.length;++i) {
|
||||
if (this._freeSounds[i]) this._freeSounds[i].stop();
|
||||
if (this._freeSounds[i]) this._freeSounds[i].unload();
|
||||
}
|
||||
for (var i = 0;i<this._freeMusics.length;++i) {
|
||||
if (this._freeMusics[i]) this._freeMusics[i].stop();
|
||||
if (this._freeMusics[i]) this._freeMusics[i].unload();
|
||||
}
|
||||
this._freeSounds.length = 0;
|
||||
this._freeMusics.length = 0;
|
||||
|
||||
for (var p in this._sounds) {
|
||||
if (this._sounds.hasOwnProperty(p) && this._sounds[p]) {
|
||||
this._sounds[p].stop();
|
||||
this._sounds[p].unload();
|
||||
delete this._sounds[p];
|
||||
}
|
||||
}
|
||||
for (var p in this._musics) {
|
||||
if (this._musics.hasOwnProperty(p) && this._musics[p]) {
|
||||
this._musics[p].stop();
|
||||
this._musics[p].unload();
|
||||
delete this._musics[p];
|
||||
}
|
||||
}
|
||||
this._pausedSounds = [];
|
||||
this._pausedSounds.length = 0;
|
||||
}
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.preloadAudio = function(onProgress, onComplete, resources) {
|
||||
resources = resources || this._resources;
|
||||
|
||||
var files = [];
|
||||
for(var i = 0, len = resources.length;i<len;++i) {
|
||||
var res = resources[i];
|
||||
@@ -319,10 +323,8 @@ gdjs.HowlerSoundManager.prototype.preloadAudio = function(onProgress, onComplete
|
||||
|
||||
var loaded = 0;
|
||||
function onLoad(audioFile) {
|
||||
console.log("loaded" + audioFile);
|
||||
loaded++;
|
||||
if (loaded === files.length) {
|
||||
console.log("All audio loaded");
|
||||
return onComplete();
|
||||
}
|
||||
|
||||
@@ -332,13 +334,12 @@ gdjs.HowlerSoundManager.prototype.preloadAudio = function(onProgress, onComplete
|
||||
var that = this;
|
||||
for(var i = 0;i<files.length;++i) {
|
||||
(function(audioFile) {
|
||||
console.log("Loading" + audioFile)
|
||||
var sound = new Howl({
|
||||
src: [audioFile], //TODO: ogg, mp3...
|
||||
preload: true,
|
||||
onload: onLoad.bind(that, audioFile),
|
||||
onloaderror: onLoad.bind(that, audioFile)
|
||||
});
|
||||
var sound = new XMLHttpRequest();
|
||||
sound.addEventListener('load', onLoad.bind(that, audioFile));
|
||||
sound.addEventListener('error', onLoad.bind(that, audioFile));
|
||||
sound.addEventListener('abort', onLoad.bind(that, audioFile));
|
||||
sound.open('GET', audioFile);
|
||||
sound.send();
|
||||
})(files[i]);
|
||||
}
|
||||
}
|
||||
|
@@ -22,13 +22,13 @@ gdjs.NightPixiFilter = function() {
|
||||
opacity: { type: '1f', value: 1 }
|
||||
};
|
||||
|
||||
PIXI.AbstractFilter.call(this,
|
||||
PIXI.Filter.call(this,
|
||||
vertexShader,
|
||||
fragmentShader,
|
||||
uniforms
|
||||
);
|
||||
}
|
||||
gdjs.NightPixiFilter.prototype = Object.create(PIXI.AbstractFilter.prototype);
|
||||
gdjs.NightPixiFilter.prototype = Object.create(PIXI.Filter.prototype);
|
||||
gdjs.NightPixiFilter.prototype.constructor = gdjs.NightPixiFilter;
|
||||
|
||||
gdjs.LightNightPixiFilter = function() {
|
||||
@@ -51,13 +51,13 @@ gdjs.LightNightPixiFilter = function() {
|
||||
opacity: { type: '1f', value: 1 }
|
||||
};
|
||||
|
||||
PIXI.AbstractFilter.call(this,
|
||||
PIXI.Filter.call(this,
|
||||
vertexShader,
|
||||
fragmentShader,
|
||||
uniforms
|
||||
);
|
||||
}
|
||||
gdjs.LightNightPixiFilter.prototype = Object.create(PIXI.AbstractFilter.prototype);
|
||||
gdjs.LightNightPixiFilter.prototype = Object.create(PIXI.Filter.prototype);
|
||||
gdjs.LightNightPixiFilter.prototype.constructor = gdjs.LightNightPixiFilter;
|
||||
|
||||
gdjs.PixiFiltersTools._filters = {
|
||||
@@ -70,7 +70,7 @@ gdjs.PixiFiltersTools._filters = {
|
||||
if (parameterName !== 'intensity' &&
|
||||
parameterName !== 'opacity') return;
|
||||
|
||||
filter.uniforms[parameterName].value = value;
|
||||
filter.uniforms[parameterName] = value;
|
||||
},
|
||||
},
|
||||
LightNight: {
|
||||
@@ -81,7 +81,7 @@ gdjs.PixiFiltersTools._filters = {
|
||||
updateParameter: function(filter, parameterName, value) {
|
||||
if (parameterName !== 'opacity') return;
|
||||
|
||||
filter.uniforms.opacity.value = value;
|
||||
filter.uniforms.opacity = value;
|
||||
},
|
||||
},
|
||||
Sepia: {
|
||||
|
@@ -68,13 +68,13 @@ gdjs.Polygon.prototype.computeEdges = function() {
|
||||
else v2 = this.vertices[i + 1];
|
||||
|
||||
this.edges[i][0] = v2[0] - v1[0];
|
||||
this.edges[i][1] = v2[1] - v1[1];
|
||||
this.edges[i][1] = v2[1] - v1[1];
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.Polygon.prototype.isConvex = function() {
|
||||
this.computeEdges();
|
||||
edgesLen = this.edges.length;
|
||||
var edgesLen = this.edges.length;
|
||||
|
||||
if ( edgesLen < 3 ) {
|
||||
return false;
|
||||
@@ -87,7 +87,7 @@ gdjs.Polygon.prototype.isConvex = function() {
|
||||
if ( (zCrossProduct > 0) !== zProductIsPositive ) return false;
|
||||
}
|
||||
|
||||
var lastZCrossProduct = this.edges[edgesLen-1][0]*this.edges[0][1] - this.edges[edgesLen][1]*this.edges[0][0];
|
||||
var lastZCrossProduct = this.edges[edgesLen-1][0]*this.edges[0][1] - this.edges[edgesLen-1][1]*this.edges[0][0];
|
||||
if ( (lastZCrossProduct > 0) !== zProductIsPositive ) return false;
|
||||
|
||||
return true;
|
||||
@@ -258,3 +258,29 @@ gdjs.Polygon.distance = function(minA, maxA, minB, maxB)
|
||||
if (minA < minB) return minB - maxA;
|
||||
else return minA - maxB;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a point is inside a polygon.
|
||||
*
|
||||
* Uses <a href="https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html">PNPOLY</a> by W. Randolph Franklin.
|
||||
*
|
||||
* @method isPointInside
|
||||
* @static
|
||||
* @param poly {Polygon} The polygon to test
|
||||
* @param x {Number} The point x coordinate
|
||||
* @param y {Number} The point y coordinate
|
||||
* @return {Boolean} true if the point is inside the polygon
|
||||
*/
|
||||
gdjs.Polygon.isPointInside = function(poly, x, y)
|
||||
{
|
||||
var inside = false;
|
||||
var vi, vj;
|
||||
for (var i = 0, j = poly.vertices.length-1; i < poly.vertices.length; j = i++) {
|
||||
vi = poly.vertices[i];
|
||||
vj = poly.vertices[j];
|
||||
if ( ((vi[1]>y) != (vj[1]>y)) && (x < (vj[0]-vi[0]) * (y-vi[1]) / (vj[1]-vi[1]) + vi[0]) )
|
||||
inside = !inside;
|
||||
}
|
||||
|
||||
return inside;
|
||||
};
|
||||
|
@@ -1202,6 +1202,23 @@ gdjs.RuntimeObject.prototype.cursorOnObject = function(runtimeScene) {
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Check if a point is inside the object collision hitboxes.
|
||||
* @method isCollidingWithPoint
|
||||
* @param pointX The point x coordinate.
|
||||
* @param pointY The point y coordinate.
|
||||
* @return true if the point is inside the object collision hitboxes.
|
||||
*/
|
||||
gdjs.RuntimeObject.prototype.isCollidingWithPoint = function(pointX, pointY) {
|
||||
var hitBoxes = this.getHitBoxes();
|
||||
for(var i = 0; i < this.hitBoxes.length; ++i) {
|
||||
if ( gdjs.Polygon.isPointInside(hitBoxes[i], pointX, pointY) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the identifier associated to an object name :<br>
|
||||
|
@@ -146,6 +146,28 @@ gdjs.RuntimeScene.prototype.unloadScene = function() {
|
||||
for(var i = 0;i < gdjs.callbacksRuntimeSceneUnloaded.length;++i) {
|
||||
gdjs.callbacksRuntimeSceneUnloaded[i](this);
|
||||
}
|
||||
|
||||
// It should not be necessary to reset these variables, but this help
|
||||
// ensuring that all memory related to the RuntimeScene is released immediately.
|
||||
this._layers = new Hashtable();
|
||||
this._variables = new gdjs.VariablesContainer();
|
||||
this._initialBehaviorSharedData = new Hashtable();
|
||||
this._objects = new Hashtable();
|
||||
this._instances = new Hashtable();
|
||||
this._instancesCache = new Hashtable();
|
||||
this._initialObjectsData = null;
|
||||
this._eventsFunction = null;
|
||||
this._objectsCtor = new Hashtable();
|
||||
this._allInstancesList = [];
|
||||
this._instancesRemoved = [];
|
||||
|
||||
this._renderer = new gdjs.RuntimeSceneRenderer(this, this._runtimeGame ? this._runtimeGame.getRenderer() : null);
|
||||
this._lastId = 0;
|
||||
this._eventsContext = null;
|
||||
|
||||
this._isLoaded = false;
|
||||
|
||||
this.onCanvasResized();
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -1,3 +1,4 @@
|
||||
#!/bin/bash
|
||||
#Get the destination, or copy by default to release directory
|
||||
DESTINATION=../../Binaries/Output/Release_Linux/JsPlatform/Runtime/
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
@@ -14,4 +15,4 @@ mkdir -p "$DESTINATION"
|
||||
cp -R ../Runtime/* "$DESTINATION"
|
||||
rsync -r -u --include=*.js --include=*/ --exclude=* ../../Extensions/ "$DESTINATION"/Extensions/
|
||||
|
||||
echo "✅ Copied GDJS and extensions runtime files (*.js) to '$DESTINATION'."
|
||||
echo "✅ Copied GDJS and extensions runtime files (*.js) to '$DESTINATION'."
|
||||
|
@@ -1,114 +0,0 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<Project>
|
||||
<GDVersion Major="3" Minor="0" Build="11297" Revision="57008" />
|
||||
<Info winExecutableFilename="" winExecutableIconFile="" linuxExecutableFilename="" macExecutableFilename="" useExternalSourceFiles="false">
|
||||
<Nom value="Projet" />
|
||||
<Auteur value="" />
|
||||
<Extensions>
|
||||
<Extension name="BuiltinObject" />
|
||||
<Extension name="BuiltinAudio" />
|
||||
<Extension name="BuiltinVariables" />
|
||||
<Extension name="BuiltinTime" />
|
||||
<Extension name="BuiltinMouse" />
|
||||
<Extension name="BuiltinKeyboard" />
|
||||
<Extension name="BuiltinJoystick" />
|
||||
<Extension name="BuiltinCamera" />
|
||||
<Extension name="BuiltinWindow" />
|
||||
<Extension name="BuiltinFile" />
|
||||
<Extension name="BuiltinNetwork" />
|
||||
<Extension name="BuiltinScene" />
|
||||
<Extension name="BuiltinAdvanced" />
|
||||
<Extension name="Sprite" />
|
||||
<Extension name="BuiltinCommonInstructions" />
|
||||
<Extension name="BuiltinCommonConversions" />
|
||||
<Extension name="BuiltinStringInstructions" />
|
||||
<Extension name="BuiltinMathematicalTools" />
|
||||
<Extension name="BuiltinExternalLayouts" />
|
||||
<Extension name="TextObject" />
|
||||
</Extensions>
|
||||
<Platforms current="GDevelop JS platform">
|
||||
<Platform name="GDevelop JS platform" />
|
||||
<Platform name="GDevelop C++ platform" />
|
||||
</Platforms>
|
||||
<WindowW value="800" />
|
||||
<WindowH value="600" />
|
||||
<Portable />
|
||||
<LatestCompilationDirectory value="" />
|
||||
<FPSmax value="60" />
|
||||
<FPSmin value="10" />
|
||||
<verticalSync value="false" />
|
||||
</Info>
|
||||
<Resources>
|
||||
<Resources />
|
||||
<ResourceFolders />
|
||||
</Resources>
|
||||
<Objects />
|
||||
<ObjectGroups />
|
||||
<Variables />
|
||||
<Scenes firstScene="">
|
||||
<Scene nom="Nouvelle sc<73>ne" mangledName="Nouvelle_32sc__4524ne" r="209.000000" v="209.000000" b="209.000000" titre="" oglFOV="90.000000" oglZNear="1.000000" oglZFar="500.000000" standardSortMethod="true" stopSoundsOnStartup="true" disableInputWhenNotFocused="true">
|
||||
<UISettings gridWidth="32.000000" grid="false" snap="true" gridHeight="32.000000" gridR="158.000000" gridG="180.000000" gridB="255.000000" zoomFactor="1.000000" windowMask="true" associatedLayout="" />
|
||||
<GroupesObjets />
|
||||
<Objets>
|
||||
<Objet nom="NouvelObjet" type="TextObject::Text" smoothed="true" bold="false" italic="false" underlined="false">
|
||||
<Variables />
|
||||
<String value="Heeello" />
|
||||
<Font value="BOD_CB.TTF" />
|
||||
<CharacterSize value="50" />
|
||||
<Color r="255" g="128" b="0" />
|
||||
</Objet>
|
||||
</Objets>
|
||||
<Layers>
|
||||
<Layer Name="" Visibility="true">
|
||||
<Camera DefaultSize="true" Width="0.000000" Height="0.000000" DefaultViewport="true" ViewportLeft="0.000000" ViewportTop="0.000000" ViewportRight="1.000000" ViewportBottom="1.000000" />
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Variables />
|
||||
<BehaviorsSharedDatas />
|
||||
<Positions>
|
||||
<Objet nom="NouvelObjet" x="297.000000" y="245.000000" plan="1" layer="" angle="0.000000" personalizedSize="false" width="0.000000" height="0.000000" locked="false">
|
||||
<floatInfos />
|
||||
<stringInfos />
|
||||
<InitialVariables />
|
||||
</Objet>
|
||||
</Positions>
|
||||
<Events>
|
||||
<Event disabled="false" folded="false">
|
||||
<Type value="BuiltinCommonInstructions::Standard" />
|
||||
<Conditions />
|
||||
<Actions>
|
||||
<Action>
|
||||
<Type value="MettreXY" />
|
||||
<Parametre value="NouvelObjet" />
|
||||
<Parametre value="=" />
|
||||
<Parametre value="300" />
|
||||
<Parametre value="=" />
|
||||
<Parametre value="300" />
|
||||
</Action>
|
||||
<Action>
|
||||
<Type value="TextObject::Angle" />
|
||||
<Parametre value="NouvelObjet" />
|
||||
<Parametre value="+" />
|
||||
<Parametre value="1" />
|
||||
</Action>
|
||||
<Action>
|
||||
<Type value="TextObject::Opacity" />
|
||||
<Parametre value="NouvelObjet" />
|
||||
<Parametre value="=" />
|
||||
<Parametre value="128+cos(TimeFromStart())*128" />
|
||||
</Action>
|
||||
<Action>
|
||||
<Type value="TextObject::Size" />
|
||||
<Parametre value="NouvelObjet" />
|
||||
<Parametre value="=" />
|
||||
<Parametre value="30+cos(TimeFromStart())*30" />
|
||||
</Action>
|
||||
</Actions>
|
||||
</Event>
|
||||
</Events>
|
||||
</Scene>
|
||||
</Scenes>
|
||||
<ExternalEvents />
|
||||
<ExternalLayouts />
|
||||
<ExternalSourceFiles />
|
||||
</Project>
|
1
GDJS/tests/games/Text.json
Normal file
@@ -0,0 +1 @@
|
||||
{"firstLayout": "","gdVersion": {"build": 96,"major": 4,"minor": 0,"revision": 89},"properties": {"folderProject": false,"linuxExecutableFilename": "","macExecutableFilename": "","packageName": "","projectFile": "/Users/florian/Projects/F/GD/GDJS/tests/games/Text.json","useExternalSourceFiles": false,"winExecutableFilename": "","winExecutableIconFile": "","name": "Projet","author": "","windowWidth": 800,"windowHeight": 600,"latestCompilationDirectory": "","maxFPS": 60,"minFPS": 10,"verticalSync": false,"extensions": [{"name": "BuiltinObject"},{"name": "BuiltinAudio"},{"name": "BuiltinVariables"},{"name": "BuiltinTime"},{"name": "BuiltinMouse"},{"name": "BuiltinKeyboard"},{"name": "BuiltinJoystick"},{"name": "BuiltinCamera"},{"name": "BuiltinWindow"},{"name": "BuiltinFile"},{"name": "BuiltinNetwork"},{"name": "BuiltinScene"},{"name": "BuiltinAdvanced"},{"name": "Sprite"},{"name": "BuiltinCommonInstructions"},{"name": "BuiltinCommonConversions"},{"name": "BuiltinStringInstructions"},{"name": "BuiltinMathematicalTools"},{"name": "BuiltinExternalLayouts"},{"name": "TextObject"}],"platforms": [{"name": "GDevelop JS platform"},{"name": "GDevelop C++ platform"}],"currentPlatform": "GDevelop C++ platform"},"resources": {"resources": [],"resourceFolders": []},"objects": [],"objectsGroups": [],"variables": [],"layouts": [{"b": 209,"disableInputWhenNotFocused": true,"mangledName": "Nouvelle_32sc_232ne","name": "Nouvelle scène","oglFOV": 90,"oglZFar": 500,"oglZNear": 1,"r": 209,"standardSortMethod": true,"stopSoundsOnStartup": true,"title": "","v": 209,"uiSettings": {"grid": false,"gridB": 255,"gridG": 180,"gridHeight": 32,"gridOffsetX": 0,"gridOffsetY": 0,"gridR": 158,"gridWidth": 32,"snap": true,"windowMask": true,"zoomFactor": 1},"objectsGroups": [],"variables": [],"instances": [{"angle": 0,"customSize": false,"height": 0,"layer": "","locked": false,"name": "NouvelObjet","width": 0,"x": 297,"y": 245,"zOrder": 1,"numberProperties": [],"stringProperties": [],"initialVariables": []}],"objects": [{"bold": false,"italic": false,"name": "NouvelObjet","smoothed": true,"type": "TextObject::Text","underlined": false,"variables": [],"behaviors": [],"string": "Heeello","font": "BOD_CB.TTF","characterSize": 50,"color": {"b": 0,"g": 128,"r": 255}}],"events": [{"disabled": false,"folded": false,"type": "BuiltinCommonInstructions::Standard","conditions": [],"actions": [{"type": {"inverted": false,"value": "MettreXY"},"parameters": ["NouvelObjet","=","300","=","300"],"subInstructions": []},{"type": {"inverted": false,"value": "TextObject::Angle"},"parameters": ["NouvelObjet","+","1"],"subInstructions": []},{"type": {"inverted": false,"value": "TextObject::Opacity"},"parameters": ["NouvelObjet","=","128+cos(TimeFromStart())*128"],"subInstructions": []},{"type": {"inverted": false,"value": "TextObject::Size"},"parameters": ["NouvelObjet","=","30+cos(TimeFromStart())*30"],"subInstructions": []}],"events": []}],"layers": [{"name": "","visibility": true,"cameras": [{"defaultSize": true,"defaultViewport": true,"height": 0,"viewportBottom": 1,"viewportLeft": 0,"viewportRight": 1,"viewportTop": 0,"width": 0}],"effects": []}],"behaviorsSharedData": []}],"externalEvents": [],"externalLayouts": [],"externalSourceFiles": []}
|
@@ -531,7 +531,7 @@ void InstructionSelectorDialog::OnOkBtClick(wxCommandEvent& event)
|
||||
||(instructionMetadata.parameters[i].type == "layer" && !expressionParser.ParseStringExpression(game.GetCurrentPlatform(), game, scene, callbacks))
|
||||
||(instructionMetadata.parameters[i].type == "expression" && !expressionParser.ParseMathExpression(game.GetCurrentPlatform(), game, scene, callbacks)))
|
||||
{
|
||||
message = expressionParser.firstErrorStr;
|
||||
message = expressionParser.GetFirstError();
|
||||
|
||||
parametersHaveErrors = true;
|
||||
ParaEdit[i]->SetBackgroundColour(wxColour(255, 194, 191));
|
||||
|
@@ -4,7 +4,7 @@ GDevelop is a full featured, open source game development software, allowing to
|
||||
|
||||

|
||||
|
||||
Getting started [](https://travis-ci.org/4ian/GD) [](https://ci.appveyor.com/project/4ian/gd)
|
||||
Getting started [](https://semaphoreci.com/4ian/gd) [](https://travis-ci.org/4ian/GD) [](https://ci.appveyor.com/project/4ian/gd)
|
||||
---------------
|
||||
|
||||
| ❔ I want to... | 🚀 What to do |
|
||||
@@ -49,7 +49,6 @@ Links
|
||||
### Related projects and games
|
||||
|
||||
* [GDevelop.js](https://github.com/4ian/GDevelop.js) is a binding to use GDevelop engine in Javascript. Used for newIDE.
|
||||
* [GDevApp.com](https://gdevapp.com) is a radically innovative online game creator, compatible with GDevelop. It is based on GDevelop.js and can be used on any browser, including iOS and Android.
|
||||
* [Lil BUB's HELLO EARTH](http://lilbub.com/game) is a retro 8-bit mobile video game featuring [Lil BUB](http://lilbub.com). It's created with GDevelop and made up of equal parts science, magic, and heart.
|
||||
|
||||

|
||||
|
@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
|
||||
project(GDVersion)
|
||||
set(GD_VERSION_STR "4.0.96")
|
||||
|
||||
if(FULL_VERSION_NUMBER)
|
||||
set(GENERATE_VERSION_SCRIPT ${PROJECT_SOURCE_DIR}/GenerateVersionFull.cmake)
|
||||
@@ -11,5 +12,5 @@ endif()
|
||||
|
||||
add_custom_target(GDVersion
|
||||
ALL
|
||||
COMMAND ${CMAKE_COMMAND} -P ${GENERATE_VERSION_SCRIPT} ${PROJECT_SOURCE_DIR}/../Core/GDCore/Tools/
|
||||
COMMAND ${CMAKE_COMMAND} -P ${GENERATE_VERSION_SCRIPT} ${PROJECT_SOURCE_DIR}/../Core/GDCore/Tools/ ${GD_VERSION_STR}
|
||||
)
|
||||
|
@@ -1,13 +1,19 @@
|
||||
find_package(Git)
|
||||
|
||||
if(GIT_FOUND)
|
||||
EXECUTE_PROCESS(
|
||||
COMMAND ${GIT_EXECUTABLE} describe --tags
|
||||
OUTPUT_VARIABLE GD_VERSION_STR
|
||||
RESULT_VARIABLE GIT_DESCRIBE_RESULT
|
||||
ERROR_VARIABLE GIT_DESCRIBE_ERROR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
# Retrieving GDevelop version from Git tags is disabled as GDevelop 5
|
||||
# is being developed and we still want the old IDE version to stay at 4.
|
||||
# The old IDE has the same version number as GDCore/libGD.js
|
||||
# Hence, version of GDevelop 4 is manually specified in CMakeLists.txt
|
||||
# EXECUTE_PROCESS(
|
||||
# COMMAND ${GIT_EXECUTABLE} describe --tags
|
||||
# OUTPUT_VARIABLE GD_VERSION_STR
|
||||
# RESULT_VARIABLE GIT_DESCRIBE_RESULT
|
||||
# ERROR_VARIABLE GIT_DESCRIBE_ERROR
|
||||
# OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
# )
|
||||
set(GD_VERSION_STR ${CMAKE_ARGV4})
|
||||
|
||||
set(VERSIONPRIV_PATH "${CMAKE_ARGV3}/VersionPriv.h")
|
||||
set(ORIGINAL_CONTENT " ")
|
||||
|
@@ -3,13 +3,19 @@ find_package(Git)
|
||||
message(WARNING "You're not using the full version number. It's not suitable for public releases and builds!")
|
||||
|
||||
if(GIT_FOUND)
|
||||
EXECUTE_PROCESS(
|
||||
COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=0 # Only get the lastest tag's name
|
||||
OUTPUT_VARIABLE GD_VERSION_STR
|
||||
RESULT_VARIABLE GIT_DESCRIBE_RESULT
|
||||
ERROR_VARIABLE GIT_DESCRIBE_ERROR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
# Retrieving GDevelop version from Git tags is disabled as GDevelop 5
|
||||
# is being developed and we still want the old IDE version to stay at 4.
|
||||
# The old IDE has the same version number as GDCore/libGD.js
|
||||
# Hence, version of GDevelop 4 is manually specified in CMakeLists.txt
|
||||
# EXECUTE_PROCESS(
|
||||
# COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=0 # Only get the lastest tag's name
|
||||
# OUTPUT_VARIABLE GD_VERSION_STR
|
||||
# RESULT_VARIABLE GIT_DESCRIBE_RESULT
|
||||
# ERROR_VARIABLE GIT_DESCRIBE_ERROR
|
||||
# OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
# )
|
||||
set(GD_VERSION_STR ${CMAKE_ARGV4})
|
||||
|
||||
set(VERSIONPRIV_PATH "${CMAKE_ARGV3}/VersionPriv.h")
|
||||
set(ORIGINAL_CONTENT " ")
|
||||
|
@@ -61,18 +61,29 @@ cd newIDE/app
|
||||
yarn test #or npm run test
|
||||
```
|
||||
|
||||
### Theming
|
||||
|
||||
It's possible to create new themes for the UI. See [this file](https://github.com/4ian/GD/blob/master/newIDE/app/src/UI/Theme/index.js) to declare a new theme. You can take a look at the [default theme](https://github.com/4ian/GD/blob/master/newIDE/app/src/UI/Theme/DefaultTheme/index.js), including the [styling of the Events Sheets](https://github.com/4ian/GD/blob/master/newIDE/app/src/UI/Theme/DefaultTheme/EventsSheet.css).
|
||||
|
||||
## Building and deploying the standalone app
|
||||
|
||||
### Desktop version
|
||||
|
||||
First, update version number which is read in `newIDE/electron-app/app/package.json`.
|
||||
|
||||
```bash
|
||||
cd newIDE/electron-app
|
||||
yarn build #or npm run build
|
||||
```
|
||||
|
||||
This will build and package the Electron app for Windows, macOS and Linux (according to your OS).
|
||||
The output are stored inside `newIDE/electron-app/dist` and copied to `Binaries/Output/Release_XXX`.
|
||||
Version number is read from `newIDE/electron-app/app/package.json`.
|
||||
This will build and package the Electron app for Windows, macOS and Linux (according to your OS). The output are stored inside `newIDE/electron-app/dist`.
|
||||
|
||||
To build artifacts for all platforms and publish to a draft GitHub release:
|
||||
|
||||
```
|
||||
GH_TOKEN=xxx yarn build --mac --win --linux tar.gz --publish always
|
||||
```
|
||||
|
||||
|
||||
### Webapp version
|
||||
|
||||
@@ -86,10 +97,9 @@ 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))
|
||||
- [ ] [Autocompletion of expressions and parameters in Events editor](https://trello.com/c/mAROBTR8/46-expression-editor-auto-complete-for-the-new-ide).
|
||||
- [ ] [Points and collision mask editor](https://trello.com/c/2Kzwj61r/47-points-and-collision-masks-editors-for-sprite-objects-in-the-new-ide)
|
||||
- [ ] [Collision mask editor](https://trello.com/c/2Kzwj61r/47-collision-masks-editors-for-sprite-objects-in-the-new-ide)
|
||||
- [ ] Support for native games
|
||||
- [ ] Export with Cocos2d-JS to Android and iOS.
|
||||
- [ ] 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
|
||||
- [ ] More [examples](https://github.com/4ian/GD/blob/master/newIDE/app/src/ProjectCreation/BrowserExamples.js)
|
||||
- [ ] More [tutorials](http://wiki.compilgames.net/doku.php/gdevelop5/start)
|
||||
|
@@ -1,5 +1,7 @@
|
||||
[ignore]
|
||||
<PROJECT_ROOT>/resources/.*
|
||||
<PROJECT_ROOT>/node_modules/auth0-lock/node_modules/fbjs/flow/include/PromiseMap.js
|
||||
<PROJECT_ROOT>/node_modules/protobufjs/src/bower.json
|
||||
|
||||
[include]
|
||||
|
||||
|
2
newIDE/app/.gitignore
vendored
@@ -5,6 +5,7 @@ node_modules
|
||||
|
||||
# testing
|
||||
coverage
|
||||
flow-coverage
|
||||
|
||||
# production
|
||||
build
|
||||
@@ -23,3 +24,4 @@ public/res
|
||||
public/CppPlatform
|
||||
public/JsPlatform
|
||||
resources/GDJS
|
||||
|
||||
|
18
newIDE/app/flow-typed/libGD.js
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
// @flow
|
||||
|
||||
//TODO: These types could be generated from GDevelop.js instead of being
|
||||
//manually written here.
|
||||
type EmscriptenObject = Object & {
|
||||
ptr: Number
|
||||
};
|
||||
|
||||
declare type gdProject = EmscriptenObject;
|
||||
declare type gdLayout = EmscriptenObject;
|
||||
declare type gdExternalLayout = EmscriptenObject;
|
||||
declare type gdExternalEvents = EmscriptenObject;
|
||||
declare type gdSerializerElement = EmscriptenObject;
|
||||
declare type gdInitialInstance = EmscriptenObject;
|
||||
declare type gdBaseEvent = EmscriptenObject;
|
||||
|
||||
//Represents all objects that have serializeTo and unserializeFrom methods.
|
||||
declare type gdSerializable = EmscriptenObject;
|
4207
newIDE/app/flow-typed/npm/lodash_v4.x.x.js
vendored
Normal file
94
newIDE/app/flow-typed/npm/react-i18next_v6.x.x.js
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
// flow-typed signature: 6c6a5771bbdffe188d60637063b5f9a4
|
||||
// flow-typed version: 6533cd10ce/react-i18next_v6.x.x/flow_>=v0.53.x
|
||||
|
||||
declare module "react-i18next" {
|
||||
declare type TFunction = (key?: ?string, data?: ?Object) => string;
|
||||
declare type Locales = string | Array<string>;
|
||||
|
||||
declare type TranslatorProps = {
|
||||
t: TFunction,
|
||||
i18nLoadedAt: Date,
|
||||
i18n: Object
|
||||
};
|
||||
|
||||
declare type Translator<OP, P> = (
|
||||
component: React$ComponentType<P>
|
||||
) => Class<React$Component<OP, *>>;
|
||||
|
||||
declare type TranslateOptions = $Shape<{
|
||||
wait: boolean,
|
||||
nsMode: "default" | "fallback",
|
||||
bindi18n: false | string,
|
||||
bindStore: false | string,
|
||||
withRef: boolean,
|
||||
translateFuncName: string,
|
||||
i18n: Object
|
||||
}>;
|
||||
|
||||
declare function translate<OP, P>(
|
||||
locales?: Locales,
|
||||
options?: TranslateOptions
|
||||
): Translator<OP, P>;
|
||||
|
||||
declare type I18nProps = {
|
||||
i18n?: Object,
|
||||
ns?: string | Array<string>,
|
||||
children: (t: TFunction, { i18n: Object, t: TFunction }) => React$Node,
|
||||
initialI18nStore?: Object,
|
||||
initialLanguage?: string
|
||||
};
|
||||
declare var I18n: React$ComponentType<I18nProps>;
|
||||
|
||||
declare type InterpolateProps = {
|
||||
className?: string,
|
||||
dangerouslySetInnerHTMLPartElement?: string,
|
||||
i18n?: Object,
|
||||
i18nKey?: string,
|
||||
options?: Object,
|
||||
parent?: string,
|
||||
style?: Object,
|
||||
t?: TFunction,
|
||||
useDangerouslySetInnerHTML?: boolean
|
||||
};
|
||||
declare var Interpolate: React$ComponentType<InterpolateProps>;
|
||||
|
||||
declare type TransProps = {
|
||||
count?: number,
|
||||
parent?: string,
|
||||
i18n?: Object,
|
||||
i18nKey?: string,
|
||||
t?: TFunction
|
||||
};
|
||||
declare var Trans: React$ComponentType<TransProps>;
|
||||
|
||||
declare type ProviderProps = { i18n: Object, children: React$Element<*> };
|
||||
declare var I18nextProvider: React$ComponentType<ProviderProps>;
|
||||
|
||||
declare type NamespacesProps = {
|
||||
components: Array<React$ComponentType<*>>,
|
||||
i18n: { loadNamespaces: Function }
|
||||
};
|
||||
declare function loadNamespaces(props: NamespacesProps): Promise<void>;
|
||||
|
||||
declare var reactI18nextModule: {
|
||||
type: "3rdParty",
|
||||
init: (instance: Object) => void
|
||||
};
|
||||
|
||||
declare var defaultOptions: {
|
||||
wait: false,
|
||||
withRef: false,
|
||||
bindI18n: "languageChanged loaded",
|
||||
bindStore: "added removed",
|
||||
translateFuncName: "t",
|
||||
nsMode: "default"
|
||||
};
|
||||
|
||||
declare function setDefaults(options: TranslateOptions): void;
|
||||
|
||||
declare function getDefaults(): TranslateOptions;
|
||||
|
||||
declare function getI18n(): Object;
|
||||
|
||||
declare function setI18n(instance: Object): void;
|
||||
}
|
@@ -5,66 +5,74 @@
|
||||
"license": "MIT",
|
||||
"homepage": ".",
|
||||
"devDependencies": {
|
||||
"flow-bin": "^0.58.0",
|
||||
"flow-bin": "^0.61.0",
|
||||
"flow-coverage-report": "^0.4.0",
|
||||
"follow-redirects": "^1.2.3",
|
||||
"prettier": "1.7.0",
|
||||
"react-scripts": "1.0.6",
|
||||
"react-scripts": "1.0.17",
|
||||
"shelljs": "^0.7.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/react": "3.1.3",
|
||||
"@storybook/react": "3.2.19",
|
||||
"aws-sdk": "^2.100.0",
|
||||
"axios": "^0.16.1",
|
||||
"classnames": "^2.2.5",
|
||||
"element-closest": "^2.0.2",
|
||||
"blueimp-md5": "^2.10.0",
|
||||
"classnames": "2.2.5",
|
||||
"date-fns": "^1.29.0",
|
||||
"element-closest": "2.0.2",
|
||||
"firebase": "^4.8.2",
|
||||
"flat": "2.0.1",
|
||||
"fontfaceobserver": "2.0.13",
|
||||
"i18next": "^10.0.3",
|
||||
"keen-tracking": "1.1.3",
|
||||
"lodash.assignin": "^4.2.0",
|
||||
"lodash.compact": "^3.0.1",
|
||||
"lodash.findindex": "^4.6.0",
|
||||
"lodash.flatten": "^4.4.0",
|
||||
"lodash.keys": "^4.2.0",
|
||||
"lodash.update": "^4.10.2",
|
||||
"lodash.values": "^4.3.0",
|
||||
"material-ui": "0.18.3",
|
||||
"material-ui-search-bar": "0.4.0",
|
||||
"lodash": "4.17.4",
|
||||
"material-ui": "0.20",
|
||||
"material-ui-search-bar": "0.4.1",
|
||||
"pixi-simple-gesture": "0.2.2",
|
||||
"pixi.js": "3.0.11",
|
||||
"prop-types": "^15.5.10",
|
||||
"raven-js": "^3.19.1",
|
||||
"react": "15.4.2",
|
||||
"react-addons-css-transition-group": "15.4.2",
|
||||
"react-addons-perf": "15.4.2",
|
||||
"react-color": "2.11.7",
|
||||
"react-dnd": "2.3.0",
|
||||
"react-dnd-html5-backend": "2.3.0",
|
||||
"react-dom": "15.4.2",
|
||||
"react-i18next": "^6.0.6",
|
||||
"react-measure": "1.4.6",
|
||||
"react-mosaic-component": "4ian/react-mosaic#fix/is-dragging",
|
||||
"react-sortable-hoc": "^0.6.3",
|
||||
"react-sortable-tree": "^0.1.21",
|
||||
"react-tap-event-plugin": "2.0.1",
|
||||
"react-test-renderer": "15.4.2",
|
||||
"react-virtualized": "9.3.0",
|
||||
"slugs": "^0.1.3",
|
||||
"react": "^16.2.0",
|
||||
"react-color": "2.13.8",
|
||||
"react-dnd": "2.5.4",
|
||||
"react-dnd-html5-backend": "2.5.4",
|
||||
"react-dom": "^16.2.0",
|
||||
"react-error-boundary": "^1.2.0",
|
||||
"react-i18next": "6.2.0",
|
||||
"react-measure": "1.4.7",
|
||||
"react-mosaic-component": "1.0.3",
|
||||
"react-sortable-hoc": "0.6.8",
|
||||
"react-sortable-tree": "1.5.3",
|
||||
"react-test-renderer": "16.2.0",
|
||||
"react-virtualized": "9.14.1",
|
||||
"slugs": "0.1.3",
|
||||
"source-map-explorer": "^1.4.0",
|
||||
"wait-promise": "^0.4.1"
|
||||
"wait-promise": "0.4.1"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "npm run import-resources",
|
||||
"import-resources": "cd scripts && node import-libGD.js && node import-res-folder.js && node import-GDJS-Runtime.js",
|
||||
"start": "npm run import-resources && react-scripts start",
|
||||
"build": "npm run import-resources && react-scripts build",
|
||||
"postinstall": "npm run import-resources && cd node_modules/react-mosaic-component && yarn && yarn build",
|
||||
"format": "prettier --write \"src/**/*.js\"",
|
||||
"test": "react-scripts test --env=jsdom",
|
||||
"flow": "flow",
|
||||
"analyze-source-map": "source-map-explorer build/static/js/main.*",
|
||||
"storybook": "start-storybook -p 9009 -s public",
|
||||
"build-storybook": "build-storybook -s public"
|
||||
"analyze-test-coverage": "react-scripts test --env=jsdom --coverage",
|
||||
"analyze-flow-coverage": "flow-coverage-report",
|
||||
"analyze-source-map": "source-map-explorer build/static/js/main.*"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"flow-coverage-report": {
|
||||
"includeGlob": [
|
||||
"src/**/*.js"
|
||||
],
|
||||
"type": [
|
||||
"text",
|
||||
"html",
|
||||
"json"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@@ -2,9 +2,9 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
<title>GDevelop</title>
|
||||
<title>GDevelop 5</title>
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
@@ -43,6 +43,10 @@
|
||||
';path=/;expires='+new Date(0).toUTCString();i=d.indexOf('.');if(i<0)break;d=d.slice(i+1)}}};
|
||||
})(window,document,window['_fs_namespace'],'script','user');
|
||||
</script>
|
||||
|
||||
<!-- Stripe.com Checkout -->
|
||||
<script src="https://checkout.stripe.com/checkout.js"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<!-- Root div used for React `App` component rendering-->
|
||||
|
18
newIDE/app/src/BehaviorsEditor/BehaviorsHelpPagePaths.js
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Return the help page for the given behavior
|
||||
* @param {*} behavior
|
||||
*/
|
||||
export const getBehaviorHelpPagePath = behavior => {
|
||||
if (!behavior) return null;
|
||||
|
||||
switch (behavior.getTypeName()) {
|
||||
case 'DraggableBehavior::Draggable':
|
||||
return '/behaviors/draggable';
|
||||
case 'PlatformBehavior::PlatformerObjectBehavior':
|
||||
return '/behaviors/platformer';
|
||||
case 'PlatformBehavior::PlatformBehavior':
|
||||
return '/behaviors/platformer';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
};
|
@@ -1,10 +1,11 @@
|
||||
import React, { Component } from 'react';
|
||||
import Dialog from '../UI/Dialog';
|
||||
import HelpButton from '../UI/HelpButton';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import Avatar from 'material-ui/Avatar';
|
||||
import { List, ListItem } from 'material-ui/List';
|
||||
import { mapFor } from '../Utils/MapFor';
|
||||
import flatten from 'lodash.flatten';
|
||||
import flatten from 'lodash/flatten';
|
||||
|
||||
const styles = {
|
||||
icon: { borderRadius: 0 },
|
||||
@@ -64,13 +65,19 @@ export default class NewBehaviorDialog extends Component {
|
||||
if (!open || !project) return null;
|
||||
|
||||
const actions = [
|
||||
<FlatButton label="Close" primary={false} onClick={onClose} />,
|
||||
<FlatButton
|
||||
key="close"
|
||||
label="Close"
|
||||
primary={false}
|
||||
onClick={onClose}
|
||||
/>,
|
||||
];
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
title="Add a new behavior to the object"
|
||||
actions={actions}
|
||||
secondaryActions={<HelpButton helpPagePath="/behaviors" />}
|
||||
open={open}
|
||||
noMargin
|
||||
autoScrollBodyContent
|
||||
|
@@ -5,10 +5,12 @@ import Delete from 'material-ui/svg-icons/action/delete';
|
||||
import IconButton from 'material-ui/IconButton';
|
||||
import EmptyMessage from '../UI/EmptyMessage';
|
||||
import MiniToolbar from '../UI/MiniToolbar';
|
||||
import HelpIcon from '../UI/HelpIcon';
|
||||
import PropertiesEditor from '../PropertiesEditor';
|
||||
import propertiesMapToSchema from '../PropertiesEditor/PropertiesMapToSchema';
|
||||
import newNameGenerator from '../Utils/NewNameGenerator';
|
||||
import NewBehaviorDialog from './NewBehaviorDialog';
|
||||
import { getBehaviorHelpPagePath } from './BehaviorsHelpPagePaths';
|
||||
|
||||
const styles = {
|
||||
addBehaviorLine: {
|
||||
@@ -169,6 +171,9 @@ export default class BehaviorsEditor extends Component {
|
||||
>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
<HelpIcon
|
||||
helpPagePath={getBehaviorHelpPagePath(behavior)}
|
||||
/>
|
||||
</span>
|
||||
</MiniToolbar>
|
||||
<div style={styles.propertiesContainer}>
|
||||
|
@@ -1,10 +0,0 @@
|
||||
export const selectableArea = 'selectable';
|
||||
export const selectedArea = 'selected';
|
||||
|
||||
export const largeSelectableArea = 'large-selectable';
|
||||
export const largeSelectedArea = 'large-selected';
|
||||
|
||||
export const background = 'background';
|
||||
|
||||
export const container = 'gd-events-sheet';
|
||||
export const eventsTree = 'events-tree';
|
16
newIDE/app/src/EventsSheet/EmptyEventsPlaceholder.js
Normal file
@@ -0,0 +1,16 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import PlaceholderMessage from '../UI/PlaceholderMessage';
|
||||
import HelpButton from '../UI/HelpButton';
|
||||
|
||||
const EmptyEventsPlaceholder = () => (
|
||||
<PlaceholderMessage>
|
||||
<p>
|
||||
There are no events here. Events are composed of conditions and actions.
|
||||
</p>
|
||||
<p>Add your first event using the first buttons of the toolbar.</p>
|
||||
<HelpButton helpPagePath="/events" />
|
||||
</PlaceholderMessage>
|
||||
);
|
||||
|
||||
export default EmptyEventsPlaceholder;
|
15
newIDE/app/src/EventsSheet/EventsTree/ClassNames.js
Normal file
@@ -0,0 +1,15 @@
|
||||
export const selectableArea = 'selectable';
|
||||
export const selectedArea = 'selected';
|
||||
|
||||
export const largeSelectableArea = 'large-selectable';
|
||||
export const largeSelectedArea = 'large-selected';
|
||||
|
||||
export const executableEventContainer = 'executable-event-container';
|
||||
export const actionsContainer = 'actions-container';
|
||||
export const conditionsContainer = 'conditions-container';
|
||||
export const subInstructionsContainer = 'sub-instructions-container';
|
||||
export const instructionParameter = 'instruction-parameter';
|
||||
|
||||
export const background = 'background';
|
||||
|
||||
export const eventsTree = 'events-tree';
|
50
newIDE/app/src/EventsSheet/EventsTree/EventHeightsCache.js
Normal file
@@ -0,0 +1,50 @@
|
||||
// @flow
|
||||
|
||||
type WatchedComponent = {
|
||||
onHeightsChanged: (Function) => void
|
||||
};
|
||||
|
||||
/**
|
||||
* Store the height of events and notify a component whenever
|
||||
* heights have changed.
|
||||
* Needed for EventsTree as we need to tell it when heights have changed
|
||||
* so it can recompute the internal row heights of the react-virtualized List.
|
||||
*/
|
||||
export default class EventHeightsCache {
|
||||
eventHeights = {};
|
||||
updateTimeoutId: ?number = null;
|
||||
component: ?WatchedComponent = null;
|
||||
|
||||
constructor(component: WatchedComponent) {
|
||||
this.component = component;
|
||||
}
|
||||
|
||||
_notifyComponent() {
|
||||
if (this.updateTimeoutId) {
|
||||
return; // An update is already scheduled.
|
||||
}
|
||||
|
||||
// Notify the component, on the next tick, that heights have changed
|
||||
this.updateTimeoutId = setTimeout(() => {
|
||||
if (this.component) {
|
||||
this.component.onHeightsChanged(() => (this.updateTimeoutId = null));
|
||||
} else {
|
||||
this.updateTimeoutId = null;
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
setEventHeight(event: gdBaseEvent, height: number) {
|
||||
const cachedHeight = this.eventHeights[event.ptr];
|
||||
if (!cachedHeight || cachedHeight !== height) {
|
||||
// console.log(event.ptr, 'has a new height', height, 'old:', cachedHeight);
|
||||
this._notifyComponent();
|
||||
}
|
||||
|
||||
this.eventHeights[event.ptr] = height;
|
||||
}
|
||||
|
||||
getEventHeight(event: gdBaseEvent): number {
|
||||
return this.eventHeights[event.ptr] || 60;
|
||||
}
|
||||
}
|
@@ -6,6 +6,7 @@ import ForEachEvent from './Renderers/ForEachEvent';
|
||||
import RepeatEvent from './Renderers/RepeatEvent';
|
||||
import WhileEvent from './Renderers/WhileEvent';
|
||||
import LinkEvent from './Renderers/LinkEvent';
|
||||
import JsCodeEvent from './Renderers/JsCodeEvent';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -17,6 +18,7 @@ export default {
|
||||
'BuiltinCommonInstructions::Repeat': RepeatEvent,
|
||||
'BuiltinCommonInstructions::While': WhileEvent,
|
||||
'BuiltinCommonInstructions::Link': LinkEvent,
|
||||
'BuiltinCommonInstructions::JsCode': JsCodeEvent,
|
||||
},
|
||||
getEventComponent: function(event) {
|
||||
if (this.components.hasOwnProperty(event.getType()))
|
@@ -1,8 +1,8 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { mapFor } from '../Utils/MapFor';
|
||||
import { mapFor } from '../../Utils/MapFor';
|
||||
import classNames from 'classnames';
|
||||
import { selectedArea, selectableArea } from './ClassNames';
|
||||
import { selectedArea, selectableArea, subInstructionsContainer, instructionParameter } from './ClassNames';
|
||||
import InstructionsList from './InstructionsList';
|
||||
const gd = global.gd;
|
||||
const instrFormatter = gd.InstructionSentenceFormatter.get();
|
||||
@@ -20,12 +20,6 @@ const styles = {
|
||||
paddingLeft: 2,
|
||||
paddingRight: 2,
|
||||
},
|
||||
subInstructionsList: {
|
||||
marginLeft: 9,
|
||||
marginTop: 1,
|
||||
borderRight: 'none',
|
||||
borderLeft: '1px solid #d3d3d3',
|
||||
},
|
||||
};
|
||||
|
||||
export default class Instruction extends Component {
|
||||
@@ -61,29 +55,20 @@ export default class Instruction extends Component {
|
||||
{mapFor(0, formattedTexts.size(), i => {
|
||||
const formatting = formattedTexts.getTextFormatting(i);
|
||||
const parameterIndex = formatting.getUserData();
|
||||
|
||||
const isParameter =
|
||||
parameterIndex >= 0 && parameterIndex < parametersCount;
|
||||
|
||||
if (!isParameter)
|
||||
return <span key={i}>{formattedTexts.getString(i)}</span>;
|
||||
|
||||
const parameterType = metadata.getParameter(parameterIndex).getType();
|
||||
return (
|
||||
<span
|
||||
key={i}
|
||||
style={{
|
||||
color:
|
||||
'rgb(' +
|
||||
formatting.getColorRed() +
|
||||
',' +
|
||||
formatting.getColorGreen() +
|
||||
',' +
|
||||
formatting.getColorBlue() +
|
||||
')',
|
||||
fontWeight: formatting.isBold() ? 'bold' : 'normal',
|
||||
fontStyle: formatting.isItalic() ? 'italic' : 'normal',
|
||||
}}
|
||||
className={classNames({
|
||||
[selectableArea]: true,
|
||||
[instructionParameter]: true,
|
||||
[parameterType]: true,
|
||||
})}
|
||||
onClick={domEvent =>
|
||||
this.props.onParameterClick(domEvent, parameterIndex)}
|
||||
@@ -149,7 +134,7 @@ export default class Instruction extends Component {
|
||||
{this._renderInstructionText(metadata)}
|
||||
{metadata.canHaveSubInstructions() && (
|
||||
<InstructionsList
|
||||
style={styles.subInstructionsList}
|
||||
extraClassName={subInstructionsContainer}
|
||||
instrsList={instruction.getSubInstructions()}
|
||||
areConditions={this.props.isCondition}
|
||||
selection={this.props.selection}
|
@@ -1,20 +1,11 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Instruction from './Instruction.js';
|
||||
import { mapFor } from '../Utils/MapFor';
|
||||
import { isInstructionSelected } from './SelectionHandler';
|
||||
import Instruction from './Instruction';
|
||||
import { mapFor } from '../../Utils/MapFor';
|
||||
import { isInstructionSelected } from '../SelectionHandler';
|
||||
import { actionsContainer, conditionsContainer } from './ClassNames';
|
||||
|
||||
const styles = {
|
||||
conditionsContainer: {
|
||||
paddingLeft: 5,
|
||||
paddingRight: 5,
|
||||
background: '#f1f2f2',
|
||||
borderRight: '1px solid #d3d3d3',
|
||||
},
|
||||
actionsContainer: {
|
||||
paddingLeft: 5,
|
||||
paddingRight: 5,
|
||||
},
|
||||
addButton: {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
@@ -43,16 +34,26 @@ export default class InstructionsList extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const instructionsListContext = {
|
||||
isCondition: this.props.areConditions,
|
||||
instrsList: this.props.instrsList,
|
||||
};
|
||||
const {
|
||||
addButtonLabel,
|
||||
areConditions,
|
||||
extraClassName,
|
||||
instrsList,
|
||||
onAddNewInstruction,
|
||||
onInstructionClick,
|
||||
onInstructionContextMenu,
|
||||
onInstructionDoubleClick,
|
||||
onInstructionsListContextMenu,
|
||||
onParameterClick,
|
||||
selection,
|
||||
style,
|
||||
} = this.props;
|
||||
|
||||
const instructions = mapFor(0, this.props.instrsList.size(), i => {
|
||||
const instruction = this.props.instrsList.get(i);
|
||||
const instructions = mapFor(0, instrsList.size(), i => {
|
||||
const instruction = instrsList.get(i);
|
||||
const instructionContext = {
|
||||
isCondition: this.props.areConditions,
|
||||
instrsList: this.props.instrsList,
|
||||
isCondition: areConditions,
|
||||
instrsList: instrsList,
|
||||
instruction,
|
||||
indexInList: i,
|
||||
};
|
||||
@@ -60,44 +61,49 @@ export default class InstructionsList extends Component {
|
||||
return (
|
||||
<Instruction
|
||||
instruction={instruction}
|
||||
isCondition={this.props.areConditions}
|
||||
instrsList={this.props.instrsList}
|
||||
isCondition={areConditions}
|
||||
instrsList={instrsList}
|
||||
index={i}
|
||||
key={instruction.ptr}
|
||||
selected={isInstructionSelected(this.props.selection, instruction)}
|
||||
onClick={() => this.props.onInstructionClick(instructionContext)}
|
||||
selected={isInstructionSelected(selection, instruction)}
|
||||
onClick={() => onInstructionClick(instructionContext)}
|
||||
onDoubleClick={() =>
|
||||
this.props.onInstructionDoubleClick(instructionContext)}
|
||||
onInstructionDoubleClick(instructionContext)}
|
||||
onContextMenu={(x, y) =>
|
||||
this.props.onInstructionContextMenu(x, y, instructionContext)}
|
||||
onInstructionContextMenu(x, y, instructionContext)}
|
||||
onParameterClick={(domEvent, parameterIndex) =>
|
||||
this.props.onParameterClick({
|
||||
onParameterClick({
|
||||
...instructionContext,
|
||||
parameterIndex,
|
||||
domEvent,
|
||||
})}
|
||||
selection={this.props.selection}
|
||||
onAddNewSubInstruction={this.props.onAddNewInstruction}
|
||||
onSubInstructionClick={this.props.onInstructionClick}
|
||||
onSubInstructionDoubleClick={this.props.onInstructionDoubleClick}
|
||||
onSubInstructionContextMenu={this.props.onInstructionContextMenu}
|
||||
selection={selection}
|
||||
onAddNewSubInstruction={onAddNewInstruction}
|
||||
onSubInstructionClick={onInstructionClick}
|
||||
onSubInstructionDoubleClick={onInstructionDoubleClick}
|
||||
onSubInstructionContextMenu={onInstructionContextMenu}
|
||||
onSubInstructionsListContextMenu={
|
||||
this.props.onInstructionsListContextMenu
|
||||
onInstructionsListContextMenu
|
||||
}
|
||||
onSubParameterClick={this.props.onParameterClick}
|
||||
onSubParameterClick={onParameterClick}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const containerStyle = this.props.areConditions
|
||||
? styles.conditionsContainer
|
||||
: styles.actionsContainer;
|
||||
|
||||
const addButtonLabel = this.props.areConditions
|
||||
const instructionsListContext = {
|
||||
isCondition: areConditions,
|
||||
instrsList: instrsList,
|
||||
};
|
||||
const addButtonDefaultLabel = areConditions
|
||||
? 'Add condition'
|
||||
: 'Add action';
|
||||
return (
|
||||
<div style={{ ...containerStyle, ...this.props.style }}>
|
||||
<div
|
||||
className={`${areConditions
|
||||
? conditionsContainer
|
||||
: actionsContainer} ${extraClassName || ''}`}
|
||||
style={style}
|
||||
>
|
||||
{instructions}
|
||||
<a
|
||||
style={styles.addButton}
|
||||
@@ -105,14 +111,14 @@ export default class InstructionsList extends Component {
|
||||
onClick={this.onAddNewInstruction}
|
||||
onContextMenu={e => {
|
||||
e.stopPropagation();
|
||||
this.props.onInstructionsListContextMenu(
|
||||
onInstructionsListContextMenu(
|
||||
e.clientX,
|
||||
e.clientY,
|
||||
instructionsListContext
|
||||
);
|
||||
}}
|
||||
>
|
||||
{this.props.addButtonLabel || addButtonLabel}
|
||||
{addButtonLabel || addButtonDefaultLabel}
|
||||
</a>
|
||||
</div>
|
||||
);
|
@@ -2,7 +2,7 @@ import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ReactDOM from 'react-dom';
|
||||
import classNames from 'classnames';
|
||||
import { rgbToHex } from '../../Utils/ColorTransformer';
|
||||
import { rgbToHex } from '../../../Utils/ColorTransformer';
|
||||
import {
|
||||
largeSelectedArea,
|
||||
largeSelectableArea,
|
||||
@@ -37,13 +37,9 @@ export default class CommentEvent extends Component {
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
editing: false,
|
||||
};
|
||||
}
|
||||
state = {
|
||||
editing: false,
|
||||
};
|
||||
|
||||
edit = () => {
|
||||
this.setState(
|
@@ -6,16 +6,16 @@ import {
|
||||
largeSelectedArea,
|
||||
largeSelectableArea,
|
||||
selectableArea,
|
||||
executableEventContainer,
|
||||
} from '../ClassNames';
|
||||
import InlinePopover from '../InlinePopover';
|
||||
import ObjectField from '../InstructionEditor/ParameterFields/ObjectField';
|
||||
import InlinePopover from '../../InlinePopover';
|
||||
import ObjectField from '../../InstructionEditor/ParameterFields/ObjectField';
|
||||
const gd = global.gd;
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
borderBottom: '1px solid #d3d3d3',
|
||||
},
|
||||
instructionsContainer: {
|
||||
display: 'flex',
|
||||
@@ -75,6 +75,7 @@ export default class ForEachEvent extends Component {
|
||||
className={classNames({
|
||||
[largeSelectableArea]: true,
|
||||
[largeSelectedArea]: this.props.selected,
|
||||
[executableEventContainer]: true,
|
||||
})}
|
||||
>
|
||||
<div
|
@@ -11,7 +11,7 @@ const gd = global.gd;
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
height: 60,
|
||||
height: 40,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
padding: 5,
|
192
newIDE/app/src/EventsSheet/EventsTree/Renderers/JsCodeEvent.js
Normal file
@@ -0,0 +1,192 @@
|
||||
import React, { Component } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import classNames from 'classnames';
|
||||
import InlinePopover from '../../InlinePopover';
|
||||
import ObjectField from '../../InstructionEditor/ParameterFields/ObjectField';
|
||||
import {
|
||||
largeSelectedArea,
|
||||
largeSelectableArea,
|
||||
selectableArea,
|
||||
} from '../ClassNames';
|
||||
import { getHelpLink } from '../../../Utils/HelpLink';
|
||||
import Window from '../../../Utils/Window';
|
||||
const gd = global.gd;
|
||||
|
||||
const fontFamily = '"Lucida Console", Monaco, monospace';
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
minHeight: 30,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
wrappingText: {
|
||||
fontFamily,
|
||||
paddingLeft: 5,
|
||||
paddingRight: 5,
|
||||
margin: 0,
|
||||
},
|
||||
text: {
|
||||
flex: 1,
|
||||
whiteSpace: 'pre-line',
|
||||
margin: 0,
|
||||
paddingLeft: 4 * 5,
|
||||
paddingRight: 5,
|
||||
fontFamily,
|
||||
},
|
||||
textArea: {
|
||||
paddingLeft: 4 * 5,
|
||||
paddingRight: 5,
|
||||
flex: 1,
|
||||
boxSizing: 'border-box',
|
||||
width: '100%',
|
||||
fontSize: 14,
|
||||
fontFamily,
|
||||
},
|
||||
comment: {
|
||||
color: '#777',
|
||||
},
|
||||
commentLink: {
|
||||
cursor: 'pointer',
|
||||
color: '#777',
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
};
|
||||
|
||||
export default class JsCodeEvent extends Component {
|
||||
state = {
|
||||
editing: false,
|
||||
anchorEl: null,
|
||||
};
|
||||
|
||||
edit = () => {
|
||||
this.setState(
|
||||
{
|
||||
editing: true,
|
||||
height: this._container.offsetHeight,
|
||||
},
|
||||
() => {
|
||||
const input = ReactDOM.findDOMNode(this._input);
|
||||
input.focus();
|
||||
input.value = gd.asJsCodeEvent(this.props.event).getInlineCode();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
endEditing = () => {
|
||||
const jsCodeEvent = gd.asJsCodeEvent(this.props.event);
|
||||
jsCodeEvent.setInlineCode(ReactDOM.findDOMNode(this._input).value);
|
||||
this.setState(
|
||||
{
|
||||
editing: false,
|
||||
},
|
||||
() => this.props.onUpdate()
|
||||
);
|
||||
};
|
||||
|
||||
editObject = domEvent => {
|
||||
this.setState({
|
||||
editingObject: true,
|
||||
anchorEl: domEvent.currentTarget,
|
||||
});
|
||||
};
|
||||
|
||||
endObjectEditing = () => {
|
||||
this.setState({
|
||||
editingObject: false,
|
||||
anchorEl: null,
|
||||
});
|
||||
};
|
||||
|
||||
openHelp = () => {
|
||||
Window.openExternalURL(getHelpLink('/events/js-code'));
|
||||
};
|
||||
|
||||
render() {
|
||||
const jsCodeEvent = gd.asJsCodeEvent(this.props.event);
|
||||
const parameterObjects = jsCodeEvent.getParameterObjects();
|
||||
const objects = (
|
||||
<span
|
||||
className={classNames({
|
||||
[selectableArea]: true,
|
||||
})}
|
||||
onClick={this.editObject}
|
||||
>
|
||||
{parameterObjects
|
||||
? `, objects /*${parameterObjects}*/`
|
||||
: ' /* No objects selected, only pass the scene as argument */'}
|
||||
</span>
|
||||
);
|
||||
const functionStart = (
|
||||
<p style={styles.wrappingText}>
|
||||
<span>{'(function(runtimeScene'}</span>
|
||||
{objects}
|
||||
<span>{') {'}</span>
|
||||
</p>
|
||||
);
|
||||
const functionEnd = (
|
||||
<p style={styles.wrappingText}>
|
||||
<span>{'})(runtimeScene'}</span>
|
||||
{objects}
|
||||
<span>{');'}</span>
|
||||
<span style={styles.comment}>
|
||||
{' // '}
|
||||
<a onClick={this.openHelp} style={styles.commentLink}>
|
||||
Read the documentation and help
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={styles.container}
|
||||
className={classNames({
|
||||
[largeSelectableArea]: true,
|
||||
[largeSelectedArea]: this.props.selected,
|
||||
})}
|
||||
ref={container => (this._container = container)}
|
||||
>
|
||||
{functionStart}
|
||||
{!this.state.editing ? (
|
||||
<p
|
||||
className={classNames({
|
||||
[selectableArea]: true,
|
||||
})}
|
||||
onClick={this.edit}
|
||||
key="p"
|
||||
style={styles.text}
|
||||
>
|
||||
{jsCodeEvent.getInlineCode()}
|
||||
</p>
|
||||
) : (
|
||||
<textarea
|
||||
key="textarea"
|
||||
type="text"
|
||||
style={{ ...styles.textArea, height: this.state.height }}
|
||||
onBlur={this.endEditing}
|
||||
ref={input => (this._input = input)}
|
||||
/>
|
||||
)}
|
||||
{functionEnd}
|
||||
<InlinePopover
|
||||
open={this.state.editingObject}
|
||||
anchorEl={this.state.anchorEl}
|
||||
onRequestClose={this.endObjectEditing}
|
||||
>
|
||||
<ObjectField
|
||||
project={this.props.project}
|
||||
layout={this.props.layout}
|
||||
value={parameterObjects}
|
||||
onChange={text => {
|
||||
jsCodeEvent.setParameterObjects(text);
|
||||
this.props.onUpdate();
|
||||
}}
|
||||
isInline
|
||||
/>
|
||||
</InlinePopover>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,13 +1,16 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import OpenInNew from 'material-ui/svg-icons/action/open-in-new';
|
||||
import IconButton from 'material-ui/IconButton';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
largeSelectedArea,
|
||||
largeSelectableArea,
|
||||
selectableArea,
|
||||
} from '../ClassNames';
|
||||
import InlinePopover from '../InlinePopover';
|
||||
import DefaultField from '../InstructionEditor/ParameterFields/DefaultField';
|
||||
import InlinePopover from '../../InlinePopover';
|
||||
import ExternalEventsField from '../../InstructionEditor/ParameterFields/ExternalEventsField';
|
||||
import { showWarningBox } from '../../../UI/Messages/MessageBox';
|
||||
const gd = global.gd;
|
||||
|
||||
const styles = {
|
||||
@@ -27,6 +30,8 @@ export default class LinkEvent extends Component {
|
||||
event: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
_externalEventsField = null;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -37,10 +42,31 @@ export default class LinkEvent extends Component {
|
||||
}
|
||||
|
||||
edit = domEvent => {
|
||||
this.setState({
|
||||
editing: true,
|
||||
anchorEl: domEvent.currentTarget,
|
||||
});
|
||||
this.setState(
|
||||
{
|
||||
editing: true,
|
||||
anchorEl: domEvent.currentTarget,
|
||||
},
|
||||
() => {
|
||||
if (this._externalEventsField) this._externalEventsField.focus();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
openTarget = () => {
|
||||
const { project, event, onOpenLayout, onOpenExternalEvents } = this.props;
|
||||
const linkEvent = gd.asLinkEvent(event);
|
||||
const target = linkEvent.getTarget();
|
||||
|
||||
if (project.hasExternalEventsNamed(target)) {
|
||||
onOpenExternalEvents(target);
|
||||
} else if (project.hasLayoutNamed(target)) {
|
||||
onOpenLayout(target);
|
||||
} else {
|
||||
showWarningBox(
|
||||
'The specified external events do not exist in the game. Be sure that the name is correctly spelled or create them using the project manager.'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
endEditing = () => {
|
||||
@@ -51,7 +77,7 @@ export default class LinkEvent extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
var linkEvent = gd.asLinkEvent(this.props.event);
|
||||
const linkEvent = gd.asLinkEvent(this.props.event);
|
||||
const target = linkEvent.getTarget();
|
||||
|
||||
return (
|
||||
@@ -73,20 +99,24 @@ export default class LinkEvent extends Component {
|
||||
{target || '< Enter the name of external events >'}
|
||||
</i>
|
||||
</span>
|
||||
<IconButton onClick={this.openTarget} disabled={!target}>
|
||||
<OpenInNew />
|
||||
</IconButton>
|
||||
<InlinePopover
|
||||
open={this.state.editing}
|
||||
anchorEl={this.state.anchorEl}
|
||||
onRequestClose={this.endEditing}
|
||||
>
|
||||
<DefaultField
|
||||
<ExternalEventsField
|
||||
project={this.props.project}
|
||||
layout={this.props.layout}
|
||||
value={target}
|
||||
onChange={text => {
|
||||
linkEvent.setTarget(text);
|
||||
this.props.onUpdate();
|
||||
}}
|
||||
isInline
|
||||
ref={externalEventsField =>
|
||||
(this._externalEventsField = externalEventsField)}
|
||||
/>
|
||||
</InlinePopover>
|
||||
</div>
|
@@ -6,16 +6,16 @@ import {
|
||||
largeSelectedArea,
|
||||
largeSelectableArea,
|
||||
selectableArea,
|
||||
executableEventContainer,
|
||||
} from '../ClassNames';
|
||||
import InlinePopover from '../InlinePopover';
|
||||
import DefaultField from '../InstructionEditor/ParameterFields/DefaultField';
|
||||
import InlinePopover from '../../InlinePopover';
|
||||
import DefaultField from '../../InstructionEditor/ParameterFields/DefaultField';
|
||||
const gd = global.gd;
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
borderBottom: '1px solid #d3d3d3',
|
||||
},
|
||||
instructionsContainer: {
|
||||
display: 'flex',
|
||||
@@ -75,6 +75,7 @@ export default class RepeatEvent extends Component {
|
||||
className={classNames({
|
||||
[largeSelectableArea]: true,
|
||||
[largeSelectedArea]: this.props.selected,
|
||||
[executableEventContainer]: true,
|
||||
})}
|
||||
>
|
||||
<div
|
@@ -1,14 +1,17 @@
|
||||
import React, { Component } from 'react';
|
||||
import InstructionsList from '../InstructionsList.js';
|
||||
import InstructionsList from '../InstructionsList';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { largeSelectedArea, largeSelectableArea } from '../ClassNames';
|
||||
import {
|
||||
largeSelectedArea,
|
||||
largeSelectableArea,
|
||||
executableEventContainer,
|
||||
} from '../ClassNames';
|
||||
const gd = global.gd;
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
display: 'flex',
|
||||
borderBottom: '1px solid #d3d3d3',
|
||||
},
|
||||
actionsList: {
|
||||
flex: 1,
|
||||
@@ -40,6 +43,7 @@ export default class StandardEvent extends Component {
|
||||
className={classNames({
|
||||
[largeSelectableArea]: true,
|
||||
[largeSelectedArea]: this.props.selected,
|
||||
[executableEventContainer]: true,
|
||||
})}
|
||||
>
|
||||
<InstructionsList
|
@@ -1,15 +1,18 @@
|
||||
import React, { Component } from 'react';
|
||||
import InstructionsList from '../InstructionsList.js';
|
||||
import InstructionsList from '../InstructionsList';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { largeSelectedArea, largeSelectableArea } from '../ClassNames';
|
||||
import {
|
||||
largeSelectedArea,
|
||||
largeSelectableArea,
|
||||
executableEventContainer,
|
||||
} from '../ClassNames';
|
||||
const gd = global.gd;
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
borderBottom: '1px solid #d3d3d3',
|
||||
},
|
||||
instructionsContainer: {
|
||||
display: 'flex',
|
||||
@@ -45,6 +48,7 @@ export default class ForEachEvent extends Component {
|
||||
className={classNames({
|
||||
[largeSelectableArea]: true,
|
||||
[largeSelectedArea]: this.props.selected,
|
||||
[executableEventContainer]: true,
|
||||
})}
|
||||
>
|
||||
<div>While these conditions are true:</div>
|
Before Width: | Height: | Size: 175 B After Width: | Height: | Size: 175 B |
@@ -1,60 +1,19 @@
|
||||
import React, { Component } from 'react';
|
||||
import muiThemeable from 'material-ui/styles/muiThemeable';
|
||||
import findIndex from 'lodash/findIndex';
|
||||
import {
|
||||
SortableTreeWithoutDndContext as SortableTree,
|
||||
SortableTreeWithoutDndContext,
|
||||
getNodeAtPath,
|
||||
} from 'react-sortable-tree';
|
||||
import EventsRenderingService from '../EventsRenderingService';
|
||||
import { mapFor } from '../../Utils/MapFor';
|
||||
import { eventsTree } from '../ClassNames';
|
||||
import findIndex from 'lodash.findindex';
|
||||
import { getInitialSelection, isEventSelected } from '../SelectionHandler';
|
||||
import EventsRenderingService from './EventsRenderingService';
|
||||
import EventHeightsCache from './EventHeightsCache';
|
||||
import { eventsTree } from './ClassNames';
|
||||
import './style.css';
|
||||
|
||||
const indentWidth = 22;
|
||||
|
||||
/**
|
||||
* Store the height of events and notify a component whenever
|
||||
* heights have changed.
|
||||
* Needed for EventsTree as we need to tell it when heights have changed
|
||||
* so it can recompute the internal row heights of the react-virtualized List.
|
||||
*/
|
||||
class EventHeightsCache {
|
||||
eventHeights = {};
|
||||
component = null;
|
||||
|
||||
constructor(component) {
|
||||
this.component = component;
|
||||
}
|
||||
|
||||
_notifyComponent() {
|
||||
if (this.updateTimeoutId) {
|
||||
return; // An update is already scheduled.
|
||||
}
|
||||
|
||||
// Notify the component, on the next tick, that heights have changed
|
||||
this.updateTimeoutId = setTimeout(() => {
|
||||
if (this.component) {
|
||||
this.component.onHeightsChanged(() => (this.updateTimeoutId = null));
|
||||
} else {
|
||||
this.updateTimeoutId = null;
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
setEventHeight(event, height) {
|
||||
const cachedHeight = this.eventHeights[event.ptr];
|
||||
if (!cachedHeight || cachedHeight !== height) {
|
||||
// console.log(event.ptr, 'has a new height', height, 'old:', cachedHeight);
|
||||
this._notifyComponent();
|
||||
}
|
||||
|
||||
this.eventHeights[event.ptr] = height;
|
||||
}
|
||||
|
||||
getEventHeight(event) {
|
||||
return this.eventHeights[event.ptr] || 60;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The component containing an event.
|
||||
* It will report the rendered event height so that the EventsTree can
|
||||
@@ -107,6 +66,8 @@ class EventContainer extends Component {
|
||||
this.props.onInstructionsListContextMenu
|
||||
}
|
||||
onParameterClick={this.props.onParameterClick}
|
||||
onOpenExternalEvents={this.props.onOpenExternalEvents}
|
||||
onOpenLayout={this.props.onOpenLayout}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -116,11 +77,19 @@ class EventContainer extends Component {
|
||||
|
||||
const getNodeKey = ({ treeIndex }) => treeIndex;
|
||||
|
||||
const ThemableSortableTree = ({ muiTheme, ...otherProps }) => (
|
||||
<SortableTreeWithoutDndContext
|
||||
className={`${eventsTree} ${muiTheme.eventsSheetRootClassName}`}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
const SortableTree = muiThemeable()(ThemableSortableTree);
|
||||
|
||||
/**
|
||||
* Display a tree of event. Builtin on react-sortable-tree so that event
|
||||
* can be drag'n'dropped and events rows are virtualized.
|
||||
*/
|
||||
export default class EventsTree extends Component {
|
||||
export default class ThemableEventsTree extends Component {
|
||||
static defaultProps = {
|
||||
selection: getInitialSelection(),
|
||||
};
|
||||
@@ -143,7 +112,7 @@ export default class EventsTree extends Component {
|
||||
*/
|
||||
onHeightsChanged(cb) {
|
||||
this.forceUpdate(() => {
|
||||
this._list.wrappedInstance.recomputeRowHeights();
|
||||
if (this._list) this._list.wrappedInstance.recomputeRowHeights();
|
||||
if (cb) cb();
|
||||
});
|
||||
}
|
||||
@@ -154,14 +123,14 @@ export default class EventsTree extends Component {
|
||||
*/
|
||||
forceEventsUpdate(cb) {
|
||||
this.setState(this._eventsToTreeData(this.props.events), () => {
|
||||
this._list.wrappedInstance.recomputeRowHeights();
|
||||
if (this._list) this._list.wrappedInstance.recomputeRowHeights();
|
||||
if (cb) cb();
|
||||
});
|
||||
}
|
||||
|
||||
scrollToEvent(event) {
|
||||
const row = this._getEventRow(event);
|
||||
if (row !== -1) this._list.wrappedInstance.scrollToRow(row);
|
||||
if (row !== -1 && this._list) this._list.wrappedInstance.scrollToRow(row);
|
||||
}
|
||||
|
||||
_getEventRow(searchedEvent) {
|
||||
@@ -262,15 +231,18 @@ export default class EventsTree extends Component {
|
||||
onEventContextMenu={(x, y) => this.props.onEventContextMenu(x, y, node)}
|
||||
onInstructionContextMenu={this.props.onInstructionContextMenu}
|
||||
onInstructionsListContextMenu={this.props.onInstructionsListContextMenu}
|
||||
onOpenExternalEvents={this.props.onOpenExternalEvents}
|
||||
onOpenLayout={this.props.onOpenLayout}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { height } = this.props;
|
||||
|
||||
return (
|
||||
<div style={{ height: this.props.height || 400 }}>
|
||||
<div style={{ height: height || 400 }}>
|
||||
<SortableTree
|
||||
className={eventsTree}
|
||||
treeData={this.state.treeData}
|
||||
scaffoldBlockPxWidth={indentWidth}
|
||||
onChange={() => {}}
|
||||
@@ -279,6 +251,8 @@ export default class EventsTree extends Component {
|
||||
canDrop={this._canDrop}
|
||||
rowHeight={({ index }) => {
|
||||
const event = this.state.flatData[index];
|
||||
if (!event) return 0;
|
||||
|
||||
return this.eventsHeightsCache.getEventHeight(event);
|
||||
}}
|
||||
reactVirtualizedListProps={{
|
||||
|
@@ -1,10 +1,5 @@
|
||||
/* This overwrite the default react-sortable-tree css to display events list */
|
||||
|
||||
.gd-events-sheet .events-tree {
|
||||
font-family: -apple-system, ".SFNSText-Regular", "San Francisco", Roboto, "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draggable handle on the left of an event
|
||||
*/
|
||||
@@ -16,6 +11,10 @@
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.gd-events-sheet .events-tree {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Container of an event line (including the scaffolding lines and the event).
|
||||
*/
|
||||
@@ -33,7 +32,7 @@
|
||||
}
|
||||
|
||||
.gd-events-sheet .rst__row {
|
||||
/* The "landing pad" highlight box is done with positionL absolute.
|
||||
/* The "landing pad" highlight box is done with position: absolute.
|
||||
* Ensure it will cover all the event row but not the scaffolding lines.
|
||||
*/
|
||||
position: relative;
|
||||
@@ -119,15 +118,6 @@
|
||||
border: 1px transparent solid;
|
||||
}
|
||||
|
||||
.gd-events-sheet .selectable:hover {
|
||||
background-color: rgba(0,0,0, 0.1);
|
||||
}
|
||||
|
||||
.gd-events-sheet .selectable.selected {
|
||||
background-color: rgba(0,0,0, 0.1);
|
||||
border: 1px #4ab0e4 solid !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* Large selectable area (events)
|
||||
*/
|
||||
@@ -135,7 +125,3 @@
|
||||
box-sizing: border-box;
|
||||
border: 1px transparent solid;
|
||||
}
|
||||
|
||||
.gd-events-sheet .large-selectable.large-selected {
|
||||
border: 1px #4ab0e4 solid !important;
|
||||
}
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@@ -4,14 +4,10 @@ import ParameterRenderingService from './InstructionEditor/ParameterRenderingSer
|
||||
const gd = global.gd;
|
||||
|
||||
export default class InlineParameterEditor extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isValid: false,
|
||||
parameterMetadata: null,
|
||||
};
|
||||
}
|
||||
state = {
|
||||
isValid: false,
|
||||
parameterMetadata: null,
|
||||
};
|
||||
|
||||
componentWillReceiveProps(newProps) {
|
||||
if (
|
||||
@@ -79,10 +75,11 @@ export default class InlineParameterEditor extends Component {
|
||||
project={this.props.project}
|
||||
layout={this.props.layout}
|
||||
value={this.props.instruction.getParameter(this.props.parameterIndex)}
|
||||
instruction={this.props.instruction}
|
||||
instructionOrExpression={this.props.instruction}
|
||||
key={this.props.instruction.ptr}
|
||||
onChange={this.props.onChange}
|
||||
ref={field => (this._field = field)}
|
||||
parameterRenderingService={ParameterRenderingService}
|
||||
isInline
|
||||
/>
|
||||
</InlinePopover>
|
||||
|
@@ -30,13 +30,13 @@ export default class InstructionEditorDialog extends React.Component {
|
||||
<FlatButton
|
||||
label="Cancel"
|
||||
primary={false}
|
||||
onTouchTap={this.props.onCancel}
|
||||
onClick={this.props.onCancel}
|
||||
/>,
|
||||
<FlatButton
|
||||
label="Ok"
|
||||
primary={true}
|
||||
keyboardFocused={false}
|
||||
onTouchTap={this.props.onSubmit}
|
||||
onClick={this.props.onSubmit}
|
||||
/>,
|
||||
];
|
||||
|
||||
|
@@ -0,0 +1,35 @@
|
||||
// @flow
|
||||
import update from 'lodash/update';
|
||||
import compact from 'lodash/compact';
|
||||
import { type InstructionOrExpressionInformation } from './InstructionOrExpressionInformation.flow.js';
|
||||
|
||||
const GROUP_DELIMITER = '/';
|
||||
|
||||
export type InstructionOrExpressionTreeNode =
|
||||
| InstructionOrExpressionInformation
|
||||
| {
|
||||
[string]: InstructionOrExpressionTreeNode,
|
||||
};
|
||||
|
||||
export const createTree = (
|
||||
allExpressions: Array<InstructionOrExpressionInformation>
|
||||
): InstructionOrExpressionTreeNode => {
|
||||
const tree = {};
|
||||
allExpressions.forEach(
|
||||
(expressionInfo: InstructionOrExpressionInformation) => {
|
||||
update(
|
||||
tree,
|
||||
compact(expressionInfo.fullGroupName.split(GROUP_DELIMITER)),
|
||||
groupInfo => {
|
||||
const existingGroupInfo = groupInfo || {};
|
||||
return {
|
||||
...existingGroupInfo,
|
||||
[expressionInfo.displayedName]: expressionInfo,
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return tree;
|
||||
};
|
@@ -0,0 +1,149 @@
|
||||
// @flow weak
|
||||
const gd = global.gd;
|
||||
|
||||
const GROUP_DELIMITER = '/';
|
||||
|
||||
const enumerateExtensionExpressions = (
|
||||
prefix,
|
||||
expressions,
|
||||
objectMetadata,
|
||||
behaviorMetadata
|
||||
) => {
|
||||
const allExpressions = [];
|
||||
|
||||
//Get the map containing the metadata of the expression provided by the extension...
|
||||
var expressionsTypes = expressions.keys();
|
||||
|
||||
//... and add each instruction
|
||||
for (var j = 0; j < expressionsTypes.size(); ++j) {
|
||||
var exprMetadata = expressions.get(expressionsTypes.get(j));
|
||||
if (!exprMetadata.isShown()) {
|
||||
//Skip hidden expressions
|
||||
continue;
|
||||
}
|
||||
|
||||
var parameters = [];
|
||||
for (var i = 0; i < exprMetadata.getParametersCount(); i++) {
|
||||
if (objectMetadata && i === 0) continue;
|
||||
if (behaviorMetadata && i <= 1) continue; //Skip object and behavior parameters
|
||||
if (exprMetadata.getParameter(i).isCodeOnly()) continue;
|
||||
|
||||
parameters.push(exprMetadata.getParameter(i));
|
||||
}
|
||||
|
||||
const displayedName = exprMetadata.getFullName();
|
||||
const groupName = exprMetadata.getGroup();
|
||||
const fullGroupName = prefix + groupName;
|
||||
|
||||
allExpressions.push({
|
||||
type: expressionsTypes.get(j),
|
||||
name: expressionsTypes.get(j),
|
||||
displayedName,
|
||||
fullGroupName,
|
||||
metadata: exprMetadata,
|
||||
parameters: parameters,
|
||||
objectMetadata: objectMetadata,
|
||||
behaviorMetadata: behaviorMetadata,
|
||||
});
|
||||
}
|
||||
|
||||
return allExpressions;
|
||||
};
|
||||
|
||||
export const enumerateExpressions = type => {
|
||||
const freeExpressions = [];
|
||||
const objectsExpressions = [];
|
||||
const behaviorsExpressions = [];
|
||||
|
||||
const allExtensions = gd
|
||||
.asPlatform(gd.JsPlatform.get())
|
||||
.getAllPlatformExtensions();
|
||||
for (var i = 0; i < allExtensions.size(); ++i) {
|
||||
var extension = allExtensions.get(i);
|
||||
var allObjectsTypes = extension.getExtensionObjectsTypes();
|
||||
var allBehaviorsTypes = extension.getBehaviorsTypes();
|
||||
|
||||
let prefix = '';
|
||||
if (allObjectsTypes.size() > 0 || allBehaviorsTypes.size() > 0) {
|
||||
prefix =
|
||||
extension.getName() === 'BuiltinObject'
|
||||
? 'Common expressions for all objects'
|
||||
: extension.getFullName();
|
||||
prefix += GROUP_DELIMITER;
|
||||
}
|
||||
|
||||
//Check which type of expression we want to autocomplete.
|
||||
var allFreeExpressionsGetter = extension.getAllExpressions;
|
||||
var allObjectExpressionsGetter = extension.getAllExpressionsForObject;
|
||||
var allBehaviorExpressionsGetter = extension.getAllExpressionsForBehavior;
|
||||
if (type === 'string') {
|
||||
allFreeExpressionsGetter = extension.getAllStrExpressions;
|
||||
allObjectExpressionsGetter = extension.getAllStrExpressionsForObject;
|
||||
allBehaviorExpressionsGetter = extension.getAllStrExpressionsForBehavior;
|
||||
}
|
||||
|
||||
//Free expressions
|
||||
freeExpressions.push.apply(
|
||||
freeExpressions,
|
||||
enumerateExtensionExpressions(
|
||||
prefix,
|
||||
allFreeExpressionsGetter.call(extension)
|
||||
)
|
||||
);
|
||||
|
||||
//Objects expressions:
|
||||
for (var j = 0; j < allObjectsTypes.size(); ++j) {
|
||||
var objMetadata = extension.getObjectMetadata(allObjectsTypes.get(j));
|
||||
objectsExpressions.push.apply(
|
||||
objectsExpressions,
|
||||
enumerateExtensionExpressions(
|
||||
prefix,
|
||||
allObjectExpressionsGetter.call(extension, allObjectsTypes.get(j)),
|
||||
objMetadata
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
//Behaviors expressions:
|
||||
for (var k = 0; k < allBehaviorsTypes.size(); ++k) {
|
||||
var autoMetadata = extension.getBehaviorMetadata(
|
||||
allBehaviorsTypes.get(k)
|
||||
);
|
||||
behaviorsExpressions.push.apply(
|
||||
behaviorsExpressions,
|
||||
enumerateExtensionExpressions(
|
||||
prefix,
|
||||
allBehaviorExpressionsGetter.call(
|
||||
extension,
|
||||
allBehaviorsTypes.get(k)
|
||||
),
|
||||
undefined,
|
||||
autoMetadata
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
allExpressions: [
|
||||
...freeExpressions,
|
||||
...objectsExpressions,
|
||||
...behaviorsExpressions,
|
||||
],
|
||||
freeExpressions,
|
||||
objectsExpressions,
|
||||
behaviorsExpressions,
|
||||
};
|
||||
};
|
||||
|
||||
export const filterExpressions = (list, searchText) => {
|
||||
if (!searchText) return list;
|
||||
const lowercaseSearchText = searchText.toLowerCase();
|
||||
|
||||
return list.filter(enumeratedExpression => {
|
||||
return (
|
||||
enumeratedExpression.type.toLowerCase().indexOf(lowercaseSearchText) !==
|
||||
-1
|
||||
);
|
||||
});
|
||||
};
|
@@ -0,0 +1,48 @@
|
||||
import {
|
||||
enumerateExpressions,
|
||||
filterExpressions,
|
||||
} from './EnumerateExpressions';
|
||||
import { createTree } from './CreateTree';
|
||||
import isObject from 'lodash/isObject';
|
||||
|
||||
describe('EnumerateObjects', () => {
|
||||
it('can enumerate and filter expressions', () => {
|
||||
const { freeExpressions, objectsExpressions } = enumerateExpressions(
|
||||
'number'
|
||||
);
|
||||
|
||||
// Should find atan, atan2, atanh math function
|
||||
expect(filterExpressions(freeExpressions, 'atan')).toHaveLength(3);
|
||||
|
||||
// Should find abs math function
|
||||
expect(filterExpressions(freeExpressions, 'abs')).toHaveLength(1);
|
||||
|
||||
expect(filterExpressions(freeExpressions, 'MouseX')).toHaveLength(1);
|
||||
expect(filterExpressions(freeExpressions, 'MouseY')).toHaveLength(1);
|
||||
|
||||
expect(filterExpressions(objectsExpressions, 'PointX')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('can create the tree of some instructions', () => {
|
||||
const stripMetadata = obj => {
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
if (
|
||||
key === 'objectMetadata' ||
|
||||
key === 'behaviorMetadata' ||
|
||||
key === 'metadata' ||
|
||||
key === 'parameters'
|
||||
) {
|
||||
delete obj[key];
|
||||
} else if (isObject(obj[key])) {
|
||||
stripMetadata(obj[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
const { objectsExpressions } = enumerateExpressions('number');
|
||||
expect(stripMetadata(createTree(objectsExpressions))).toMatchSnapshot();
|
||||
});
|
||||
});
|
@@ -0,0 +1,95 @@
|
||||
// @flow
|
||||
import { type InstructionOrExpressionInformation } from './InstructionOrExpressionInformation.flow.js';
|
||||
const gd = global.gd;
|
||||
|
||||
const GROUP_DELIMITER = '/';
|
||||
|
||||
const enumerateExtensionInstructions = (
|
||||
groupPrefix: string,
|
||||
extensionInstructions
|
||||
) => {
|
||||
//Get the map containing the metadata of the instructions provided by the extension...
|
||||
var instructionsTypes = extensionInstructions.keys();
|
||||
const allInstructions = [];
|
||||
|
||||
//... and add each instruction
|
||||
for (let j = 0; j < instructionsTypes.size(); ++j) {
|
||||
const instrMetadata = extensionInstructions.get(instructionsTypes.get(j));
|
||||
if (instrMetadata.isHidden()) continue;
|
||||
|
||||
const displayedName = instrMetadata.getFullName();
|
||||
const groupName = instrMetadata.getGroup();
|
||||
const fullGroupName = groupPrefix + groupName;
|
||||
|
||||
allInstructions.push({
|
||||
type: instructionsTypes.get(j),
|
||||
displayedName,
|
||||
fullGroupName,
|
||||
});
|
||||
}
|
||||
|
||||
return allInstructions;
|
||||
};
|
||||
|
||||
export const enumerateInstructions = (
|
||||
isCondition: boolean
|
||||
): Array<InstructionOrExpressionInformation> => {
|
||||
let allInstructions = [];
|
||||
|
||||
const allExtensions = gd
|
||||
.asPlatform(gd.JsPlatform.get())
|
||||
.getAllPlatformExtensions();
|
||||
for (let i = 0; i < allExtensions.size(); ++i) {
|
||||
const extension = allExtensions.get(i);
|
||||
const allObjectsTypes = extension.getExtensionObjectsTypes();
|
||||
const allBehaviorsTypes = extension.getBehaviorsTypes();
|
||||
|
||||
let prefix = '';
|
||||
if (allObjectsTypes.size() > 0 || allBehaviorsTypes.size() > 0) {
|
||||
prefix =
|
||||
extension.getName() === 'BuiltinObject'
|
||||
? 'Common ' +
|
||||
(isCondition ? 'conditions' : 'action') +
|
||||
' for all objects'
|
||||
: extension.getFullName();
|
||||
prefix += GROUP_DELIMITER;
|
||||
}
|
||||
|
||||
//Free instructions
|
||||
allInstructions = [
|
||||
...allInstructions,
|
||||
...enumerateExtensionInstructions(
|
||||
prefix,
|
||||
isCondition ? extension.getAllConditions() : extension.getAllActions()
|
||||
),
|
||||
];
|
||||
|
||||
//Objects instructions:
|
||||
for (let j = 0; j < allObjectsTypes.size(); ++j) {
|
||||
allInstructions = [
|
||||
...allInstructions,
|
||||
...enumerateExtensionInstructions(
|
||||
prefix,
|
||||
isCondition
|
||||
? extension.getAllConditionsForObject(allObjectsTypes.get(j))
|
||||
: extension.getAllActionsForObject(allObjectsTypes.get(j))
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
//Behaviors instructions:
|
||||
for (let j = 0; j < allBehaviorsTypes.size(); ++j) {
|
||||
allInstructions = [
|
||||
...allInstructions,
|
||||
...enumerateExtensionInstructions(
|
||||
prefix,
|
||||
isCondition
|
||||
? extension.getAllConditionsForBehavior(allBehaviorsTypes.get(j))
|
||||
: extension.getAllActionsForBehavior(allBehaviorsTypes.get(j))
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return allInstructions;
|
||||
};
|
@@ -0,0 +1,19 @@
|
||||
import { createTree } from './CreateTree';
|
||||
import { enumerateInstructions } from './EnumerateInstructions';
|
||||
|
||||
describe('EnumerateInstructions', () => {
|
||||
it('can enumerate instructions being conditions', () => {
|
||||
const instructions = enumerateInstructions(true);
|
||||
expect(instructions).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('can enumerate instructions being actions', () => {
|
||||
const instructions = enumerateInstructions(false);
|
||||
expect(instructions).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('can create the tree of instructions', () => {
|
||||
const instructions = enumerateInstructions('number');
|
||||
expect(createTree(instructions)).toMatchSnapshot();
|
||||
});
|
||||
});
|
@@ -0,0 +1,31 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import { enumerateExpressions } from './EnumerateExpressions';
|
||||
import InstructionOrExpressionSelector from './index';
|
||||
import { createTree, type InstructionOrExpressionTreeNode } from './CreateTree';
|
||||
import { type InstructionOrExpressionInformation } from './InstructionOrExpressionInformation.flow.js';
|
||||
|
||||
export default class ExpressionSelector extends Component<*, *> {
|
||||
instructionsInfo: Array<InstructionOrExpressionInformation> = [];
|
||||
instructionsInfoTree: ?InstructionOrExpressionTreeNode = null;
|
||||
|
||||
static defaultProps = {
|
||||
expressionType: 'number',
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
const { allExpressions } = enumerateExpressions(this.props.expressionType);
|
||||
this.instructionsInfo = allExpressions;
|
||||
this.instructionsInfoTree = createTree(allExpressions);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<InstructionOrExpressionSelector
|
||||
instructionsInfo={this.instructionsInfo}
|
||||
instructionsInfoTree={this.instructionsInfoTree}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
//@flow
|
||||
|
||||
export type InstructionOrExpressionInformation = {
|
||||
type: string,
|
||||
name?: string, //For expressions
|
||||
displayedName: string,
|
||||
fullGroupName: string,
|
||||
metadata: Object,
|
||||
parameters?: Array<any>,
|
||||
objectMetadata?: Object, //For expressions
|
||||
behaviorMetadata?: Object, //For expressions
|
||||
};
|
@@ -0,0 +1,32 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import InstructionOrExpressionSelector from './index';
|
||||
import { createTree, type InstructionOrExpressionTreeNode } from './CreateTree';
|
||||
import { enumerateInstructions } from './EnumerateInstructions';
|
||||
import { type InstructionOrExpressionInformation } from './InstructionOrExpressionInformation.flow.js';
|
||||
|
||||
type Props = {
|
||||
isCondition: boolean,
|
||||
// And props from InstructionOrExpressionSelector
|
||||
};
|
||||
|
||||
export default class InstructionSelector extends Component<Props, *> {
|
||||
instructionsInfo: Array<InstructionOrExpressionInformation> = [];
|
||||
instructionsInfoTree: ?InstructionOrExpressionTreeNode = null;
|
||||
|
||||
componentWillMount() {
|
||||
const allInstructions = enumerateInstructions(this.props.isCondition);
|
||||
this.instructionsInfo = allInstructions;
|
||||
this.instructionsInfoTree = createTree(allInstructions);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<InstructionOrExpressionSelector
|
||||
instructionsInfo={this.instructionsInfo}
|
||||
instructionsInfoTree={this.instructionsInfoTree}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,180 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`EnumerateObjects can create the tree of some instructions 1`] = `
|
||||
Object {
|
||||
"Common expressions for all objects": Object {
|
||||
"Angle": Object {
|
||||
"Angle": Object {
|
||||
"displayedName": "Angle",
|
||||
"fullGroupName": "Common expressions for all objects/Angle",
|
||||
"name": "Angle",
|
||||
"type": "Angle",
|
||||
},
|
||||
},
|
||||
"Movement": Object {
|
||||
"Average X coordinates of forces": Object {
|
||||
"displayedName": "Average X coordinates of forces",
|
||||
"fullGroupName": "Common expressions for all objects/Movement",
|
||||
"name": "ForceX",
|
||||
"type": "ForceX",
|
||||
},
|
||||
"Average Y coordinates of forces": Object {
|
||||
"displayedName": "Average Y coordinates of forces",
|
||||
"fullGroupName": "Common expressions for all objects/Movement",
|
||||
"name": "ForceY",
|
||||
"type": "ForceY",
|
||||
},
|
||||
"Average angle of the forces": Object {
|
||||
"displayedName": "Average angle of the forces",
|
||||
"fullGroupName": "Common expressions for all objects/Movement",
|
||||
"name": "ForceAngle",
|
||||
"type": "ForceAngle",
|
||||
},
|
||||
"Average length of the forces": Object {
|
||||
"displayedName": "Average length of the forces",
|
||||
"fullGroupName": "Common expressions for all objects/Movement",
|
||||
"name": "ForceLength",
|
||||
"type": "ForceLength",
|
||||
},
|
||||
},
|
||||
"Position": Object {
|
||||
"Distance between two objects": Object {
|
||||
"displayedName": "Distance between two objects",
|
||||
"fullGroupName": "Common expressions for all objects/Position",
|
||||
"name": "Distance",
|
||||
"type": "Distance",
|
||||
},
|
||||
"Square distance between two objects": Object {
|
||||
"displayedName": "Square distance between two objects",
|
||||
"fullGroupName": "Common expressions for all objects/Position",
|
||||
"name": "SqDistance",
|
||||
"type": "SqDistance",
|
||||
},
|
||||
"X position": Object {
|
||||
"displayedName": "X position",
|
||||
"fullGroupName": "Common expressions for all objects/Position",
|
||||
"name": "X",
|
||||
"type": "X",
|
||||
},
|
||||
"Y position": Object {
|
||||
"displayedName": "Y position",
|
||||
"fullGroupName": "Common expressions for all objects/Position",
|
||||
"name": "Y",
|
||||
"type": "Y",
|
||||
},
|
||||
},
|
||||
"Size": Object {
|
||||
"Height": Object {
|
||||
"displayedName": "Height",
|
||||
"fullGroupName": "Common expressions for all objects/Size",
|
||||
"name": "Height",
|
||||
"type": "Height",
|
||||
},
|
||||
"Width": Object {
|
||||
"displayedName": "Width",
|
||||
"fullGroupName": "Common expressions for all objects/Size",
|
||||
"name": "Width",
|
||||
"type": "Width",
|
||||
},
|
||||
},
|
||||
"Variables": Object {
|
||||
"Object's variable": Object {
|
||||
"displayedName": "Object's variable",
|
||||
"fullGroupName": "Common expressions for all objects/Variables",
|
||||
"name": "Variable",
|
||||
"type": "Variable",
|
||||
},
|
||||
"Object's variable number of children": Object {
|
||||
"displayedName": "Object's variable number of children",
|
||||
"fullGroupName": "Common expressions for all objects/Variables",
|
||||
"name": "VariableChildCount",
|
||||
"type": "VariableChildCount",
|
||||
},
|
||||
},
|
||||
"Visibility": Object {
|
||||
"Z order": Object {
|
||||
"displayedName": "Z order",
|
||||
"fullGroupName": "Common expressions for all objects/Visibility",
|
||||
"name": "ZOrder",
|
||||
"type": "ZOrder",
|
||||
},
|
||||
},
|
||||
},
|
||||
"Sprite": Object {
|
||||
"Animations and images": Object {
|
||||
"Animation": Object {
|
||||
"displayedName": "Animation",
|
||||
"fullGroupName": "Sprite/Animations and images",
|
||||
"name": "Animation",
|
||||
"type": "Animation",
|
||||
},
|
||||
"Animation speed scale": Object {
|
||||
"displayedName": "Animation speed scale",
|
||||
"fullGroupName": "Sprite/Animations and images",
|
||||
"name": "AnimationSpeedScale",
|
||||
"type": "AnimationSpeedScale",
|
||||
},
|
||||
"Image": Object {
|
||||
"displayedName": "Image",
|
||||
"fullGroupName": "Sprite/Animations and images",
|
||||
"name": "Sprite",
|
||||
"type": "Sprite",
|
||||
},
|
||||
},
|
||||
"Direction": Object {
|
||||
"Direction": Object {
|
||||
"displayedName": "Direction",
|
||||
"fullGroupName": "Sprite/Direction",
|
||||
"name": "Direction",
|
||||
"type": "Direction",
|
||||
},
|
||||
},
|
||||
"Position": Object {
|
||||
"X position of a point": Object {
|
||||
"displayedName": "X position of a point",
|
||||
"fullGroupName": "Sprite/Position",
|
||||
"name": "PointX",
|
||||
"type": "PointX",
|
||||
},
|
||||
"Y position of a point": Object {
|
||||
"displayedName": "Y position of a point",
|
||||
"fullGroupName": "Sprite/Position",
|
||||
"name": "PointY",
|
||||
"type": "PointY",
|
||||
},
|
||||
},
|
||||
"Size": Object {
|
||||
"Scale of the height of an object": Object {
|
||||
"displayedName": "Scale of the height of an object",
|
||||
"fullGroupName": "Sprite/Size",
|
||||
"name": "ScaleY",
|
||||
"type": "ScaleY",
|
||||
},
|
||||
"Scale of the width of an object": Object {
|
||||
"displayedName": "Scale of the width of an object",
|
||||
"fullGroupName": "Sprite/Size",
|
||||
"name": "ScaleX",
|
||||
"type": "ScaleX",
|
||||
},
|
||||
},
|
||||
},
|
||||
"Text object": Object {
|
||||
"Opacity": Object {
|
||||
"Opacity": Object {
|
||||
"displayedName": "Opacity",
|
||||
"fullGroupName": "Text object/Opacity",
|
||||
"name": "Opacity",
|
||||
"type": "Opacity",
|
||||
},
|
||||
},
|
||||
"Rotation": Object {
|
||||
"Angle": Object {
|
||||
"displayedName": "Angle",
|
||||
"fullGroupName": "Text object/Rotation",
|
||||
"name": "Angle",
|
||||
"type": "Angle",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
@@ -0,0 +1,141 @@
|
||||
import React, { Component } from 'react';
|
||||
import { List, ListItem, makeSelectable } from 'material-ui/List';
|
||||
import SearchBar from 'material-ui-search-bar';
|
||||
import keys from 'lodash/keys';
|
||||
import muiThemeable from 'material-ui/styles/muiThemeable';
|
||||
const SelectableList = makeSelectable(List);
|
||||
|
||||
const styles = {
|
||||
searchBar: {
|
||||
margin: '0 auto',
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
groupListItemNestedList: {
|
||||
padding: 0,
|
||||
},
|
||||
};
|
||||
|
||||
class ThemableInstructionOrExpressionSelector extends Component {
|
||||
state = {
|
||||
search: '',
|
||||
searchResults: [],
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this._searchBar.focus();
|
||||
}
|
||||
|
||||
focus = () => {
|
||||
if (this._searchBar) this._searchBar.focus();
|
||||
};
|
||||
|
||||
_matchCritera(instructionInfo, lowercaseSearch) {
|
||||
const { displayedName, fullGroupName } = instructionInfo;
|
||||
return (
|
||||
displayedName.toLowerCase().indexOf(lowercaseSearch) !== -1 ||
|
||||
fullGroupName.toLowerCase().indexOf(lowercaseSearch) !== -1
|
||||
);
|
||||
}
|
||||
|
||||
_computeSearchResults = search => {
|
||||
const lowercaseSearch = this.state.search.toLowerCase();
|
||||
return keys(this.props.instructionsInfo)
|
||||
.map(key => {
|
||||
return this.props.instructionsInfo[key];
|
||||
})
|
||||
.filter(instructionInfo =>
|
||||
this._matchCritera(instructionInfo, lowercaseSearch)
|
||||
);
|
||||
};
|
||||
|
||||
_onSubmitSearch = () => {
|
||||
const { searchResults } = this.state;
|
||||
if (!searchResults.length) return;
|
||||
|
||||
this.props.onChoose(searchResults[0].type, searchResults[0]);
|
||||
};
|
||||
|
||||
_renderTree(instructionInfoTree) {
|
||||
const { muiTheme } = this.props;
|
||||
|
||||
return Object.keys(instructionInfoTree).map(key => {
|
||||
const instructionOrGroup = instructionInfoTree[key];
|
||||
|
||||
if (instructionOrGroup.hasOwnProperty('type')) {
|
||||
return (
|
||||
<ListItem
|
||||
key={key}
|
||||
primaryText={key}
|
||||
value={instructionOrGroup.type}
|
||||
onClick={() => {
|
||||
this.props.onChoose(instructionOrGroup.type, instructionOrGroup);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<ListItem
|
||||
key={key}
|
||||
style={{borderBottom: `1px solid ${muiTheme.listItem.separatorColor}`}}
|
||||
nestedListStyle={styles.groupListItemNestedList}
|
||||
primaryText={<div style={{color: muiTheme.listItem.groupTextColor}}>{key}</div>}
|
||||
primaryTogglesNestedList={true}
|
||||
autoGenerateNestedIndicator={true}
|
||||
nestedItems={this._renderTree(instructionOrGroup)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_renderSearchResults = () => {
|
||||
return this.state.searchResults.map(instructionInfo => {
|
||||
return (
|
||||
<ListItem
|
||||
key={instructionInfo.type}
|
||||
style={styles.listItem}
|
||||
primaryText={instructionInfo.displayedName}
|
||||
secondaryText={instructionInfo.fullGroupName}
|
||||
value={instructionInfo.type}
|
||||
onClick={() => {
|
||||
this.props.onChoose(instructionInfo.type, instructionInfo);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { muiTheme, selectedType, instructionsInfoTree, style } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: muiTheme.list.itemsBackgroundColor,
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
<SearchBar
|
||||
onChange={text =>
|
||||
this.setState({
|
||||
search: text,
|
||||
searchResults: this._computeSearchResults(text),
|
||||
})}
|
||||
onRequestSearch={this._onSubmitSearch}
|
||||
style={styles.searchBar}
|
||||
ref={searchBar => (this._searchBar = searchBar)}
|
||||
/>
|
||||
<SelectableList value={selectedType}>
|
||||
{this.state.search
|
||||
? this._renderSearchResults()
|
||||
: this._renderTree(instructionsInfoTree)}
|
||||
</SelectableList>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const InstructionOrExpressionSelector = muiThemeable()(
|
||||
ThemableInstructionOrExpressionSelector
|
||||
);
|
||||
export default InstructionOrExpressionSelector;
|
@@ -49,7 +49,7 @@ export default class InstructionParametersEditor extends Component {
|
||||
<EmptyMessage>
|
||||
{this.props.isCondition
|
||||
? 'Choose a condition on the left'
|
||||
: 'Choose a condition on the right'}
|
||||
: 'Choose an action on the left'}
|
||||
</EmptyMessage>
|
||||
</div>
|
||||
);
|
||||
@@ -98,12 +98,13 @@ export default class InstructionParametersEditor extends Component {
|
||||
project={project}
|
||||
layout={layout}
|
||||
value={instruction.getParameter(i)}
|
||||
instruction={instruction}
|
||||
instructionOrExpression={instruction}
|
||||
key={i}
|
||||
onChange={value => {
|
||||
instruction.setParameter(i, value);
|
||||
this.forceUpdate();
|
||||
}}
|
||||
parameterRenderingService={ParameterRenderingService}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|