mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
1 Commits
v5.0.0-bet
...
feature/ne
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f368660cf2 |
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
@@ -62,12 +62,7 @@
|
||||
"__tuple": "cpp",
|
||||
"hash_map": "cpp",
|
||||
"hash_set": "cpp",
|
||||
"system_error": "cpp",
|
||||
"__nullptr": "cpp",
|
||||
"__functional_base": "cpp",
|
||||
"__functional_base_03": "cpp",
|
||||
"chrono": "cpp",
|
||||
"ratio": "cpp"
|
||||
"system_error": "cpp"
|
||||
},
|
||||
"files.exclude": {
|
||||
"Binaries/*build*": true,
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 15 KiB |
Binary file not shown.
Before Width: | Height: | Size: 324 B |
Binary file not shown.
Before Width: | Height: | Size: 382 B |
@@ -621,18 +621,6 @@ 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"));
|
||||
|
||||
|
@@ -143,13 +143,13 @@ void InstructionSentenceFormatter::LoadTypesFormattingFromConfig()
|
||||
{
|
||||
//Load default configuration
|
||||
typesFormatting.clear();
|
||||
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();
|
||||
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();
|
||||
|
||||
//Load any existing custom configuration
|
||||
#if !defined(GD_NO_WX_GUI)
|
||||
|
@@ -29,21 +29,13 @@ gdjs.TextRuntimeObjectPixiRenderer.prototype.ensureUpToDate = function() {
|
||||
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype.updateStyle = function() {
|
||||
var fontName = "\"gdjs_font_" + this._object._fontName + "\"";
|
||||
|
||||
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;
|
||||
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;
|
||||
};
|
||||
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype.updatePosition = function() {
|
||||
|
@@ -69,7 +69,6 @@ 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,19 +128,3 @@ 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,16 +33,5 @@ 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,17 +379,6 @@ 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,14 +223,6 @@ 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 bash "CopyRuntimeToGD.sh" ${GD_base_dir}/Binaries/Output/${CMAKE_BUILD_TYPE}_${CMAKE_SYSTEM_NAME}/JsPlatform/Runtime
|
||||
COMMAND sh "CopyRuntimeToGD.sh" ${GD_base_dir}/Binaries/Output/${CMAKE_BUILD_TYPE}_${CMAKE_SYSTEM_NAME}/JsPlatform/Runtime
|
||||
WORKING_DIRECTORY ${GD_base_dir}/GDJS/scripts)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
@@ -71,7 +71,6 @@ 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");
|
||||
|
@@ -100,17 +100,6 @@ 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 string.
|
||||
* Convert a rgb color value to a hex value.
|
||||
* @note No "#" or "0x" are added.
|
||||
* @static
|
||||
*/
|
||||
@@ -25,14 +25,6 @@ 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,8 +75,6 @@ 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.
|
||||
@@ -123,7 +121,7 @@ gdjs.HowlerSoundManager = function(resources)
|
||||
sound.play();
|
||||
}
|
||||
}
|
||||
that._pausedSounds.length = 0;
|
||||
that._pausedSounds = [];
|
||||
that._paused = false;
|
||||
}, false);
|
||||
});
|
||||
@@ -205,7 +203,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.unload();
|
||||
oldSound.stop();
|
||||
}
|
||||
|
||||
var soundFile = this._getFileFromSoundName(soundName);
|
||||
@@ -246,7 +244,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.unload();
|
||||
oldMusic.stop();
|
||||
}
|
||||
|
||||
var soundFile = this._getFileFromSoundName(soundName);
|
||||
@@ -270,43 +268,41 @@ gdjs.HowlerSoundManager.prototype.getMusicOnChannel = function(channel) {
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.setGlobalVolume = function(volume) {
|
||||
this._globalVolume = volume;
|
||||
if (this._globalVolume > 100) this._globalVolume = 100;
|
||||
if (this._globalVolume < 0) this._globalVolume = 0;
|
||||
Howler.volume(this._globalVolume/100);
|
||||
Howler.volume(volume/100);
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.getGlobalVolume = function() {
|
||||
return this._globalVolume;
|
||||
return Howler.volume()*100;
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.clearAll = function() {
|
||||
for (var i = 0;i<this._freeSounds.length;++i) {
|
||||
if (this._freeSounds[i]) this._freeSounds[i].unload();
|
||||
if (this._freeSounds[i]) this._freeSounds[i].stop();
|
||||
}
|
||||
for (var i = 0;i<this._freeMusics.length;++i) {
|
||||
if (this._freeMusics[i]) this._freeMusics[i].unload();
|
||||
if (this._freeMusics[i]) this._freeMusics[i].stop();
|
||||
}
|
||||
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].unload();
|
||||
this._sounds[p].stop();
|
||||
delete this._sounds[p];
|
||||
}
|
||||
}
|
||||
for (var p in this._musics) {
|
||||
if (this._musics.hasOwnProperty(p) && this._musics[p]) {
|
||||
this._musics[p].unload();
|
||||
this._musics[p].stop();
|
||||
delete this._musics[p];
|
||||
}
|
||||
}
|
||||
this._pausedSounds.length = 0;
|
||||
this._pausedSounds = [];
|
||||
}
|
||||
|
||||
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];
|
||||
@@ -323,8 +319,10 @@ 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();
|
||||
}
|
||||
|
||||
@@ -334,12 +332,13 @@ gdjs.HowlerSoundManager.prototype.preloadAudio = function(onProgress, onComplete
|
||||
var that = this;
|
||||
for(var i = 0;i<files.length;++i) {
|
||||
(function(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();
|
||||
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)
|
||||
});
|
||||
})(files[i]);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@@ -22,13 +22,13 @@ gdjs.NightPixiFilter = function() {
|
||||
opacity: { type: '1f', value: 1 }
|
||||
};
|
||||
|
||||
PIXI.Filter.call(this,
|
||||
PIXI.AbstractFilter.call(this,
|
||||
vertexShader,
|
||||
fragmentShader,
|
||||
uniforms
|
||||
);
|
||||
}
|
||||
gdjs.NightPixiFilter.prototype = Object.create(PIXI.Filter.prototype);
|
||||
gdjs.NightPixiFilter.prototype = Object.create(PIXI.AbstractFilter.prototype);
|
||||
gdjs.NightPixiFilter.prototype.constructor = gdjs.NightPixiFilter;
|
||||
|
||||
gdjs.LightNightPixiFilter = function() {
|
||||
@@ -51,13 +51,13 @@ gdjs.LightNightPixiFilter = function() {
|
||||
opacity: { type: '1f', value: 1 }
|
||||
};
|
||||
|
||||
PIXI.Filter.call(this,
|
||||
PIXI.AbstractFilter.call(this,
|
||||
vertexShader,
|
||||
fragmentShader,
|
||||
uniforms
|
||||
);
|
||||
}
|
||||
gdjs.LightNightPixiFilter.prototype = Object.create(PIXI.Filter.prototype);
|
||||
gdjs.LightNightPixiFilter.prototype = Object.create(PIXI.AbstractFilter.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;
|
||||
filter.uniforms[parameterName].value = value;
|
||||
},
|
||||
},
|
||||
LightNight: {
|
||||
@@ -81,7 +81,7 @@ gdjs.PixiFiltersTools._filters = {
|
||||
updateParameter: function(filter, parameterName, value) {
|
||||
if (parameterName !== 'opacity') return;
|
||||
|
||||
filter.uniforms.opacity = value;
|
||||
filter.uniforms.opacity.value = value;
|
||||
},
|
||||
},
|
||||
Sepia: {
|
||||
|
File diff suppressed because one or more lines are too long
@@ -258,29 +258,3 @@ 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,23 +1202,6 @@ 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,28 +146,6 @@ 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,4 +1,3 @@
|
||||
#!/bin/bash
|
||||
#Get the destination, or copy by default to release directory
|
||||
DESTINATION=../../Binaries/Output/Release_Linux/JsPlatform/Runtime/
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
@@ -15,4 +14,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'."
|
114
GDJS/tests/games/Text.gdg
Normal file
114
GDJS/tests/games/Text.gdg
Normal file
@@ -0,0 +1,114 @@
|
||||
<?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 +0,0 @@
|
||||
{"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": []}
|
@@ -2,7 +2,6 @@ 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)
|
||||
@@ -12,5 +11,5 @@ endif()
|
||||
|
||||
add_custom_target(GDVersion
|
||||
ALL
|
||||
COMMAND ${CMAKE_COMMAND} -P ${GENERATE_VERSION_SCRIPT} ${PROJECT_SOURCE_DIR}/../Core/GDCore/Tools/ ${GD_VERSION_STR}
|
||||
COMMAND ${CMAKE_COMMAND} -P ${GENERATE_VERSION_SCRIPT} ${PROJECT_SOURCE_DIR}/../Core/GDCore/Tools/
|
||||
)
|
||||
|
@@ -1,19 +1,13 @@
|
||||
find_package(Git)
|
||||
|
||||
if(GIT_FOUND)
|
||||
|
||||
# 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})
|
||||
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(VERSIONPRIV_PATH "${CMAKE_ARGV3}/VersionPriv.h")
|
||||
set(ORIGINAL_CONTENT " ")
|
||||
|
@@ -3,19 +3,13 @@ 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)
|
||||
|
||||
# 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})
|
||||
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(VERSIONPRIV_PATH "${CMAKE_ARGV3}/VersionPriv.h")
|
||||
set(ORIGINAL_CONTENT " ")
|
||||
|
@@ -61,29 +61,18 @@ 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`.
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
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`.
|
||||
|
||||
### Webapp version
|
||||
|
||||
@@ -97,6 +86,7 @@ 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).
|
||||
- [ ] [Collision mask editor](https://trello.com/c/2Kzwj61r/47-collision-masks-editors-for-sprite-objects-in-the-new-ide)
|
||||
- [ ] Support for native games
|
||||
- [ ] More [documentation](http://wiki.compilgames.net/doku.php/gdevelop5/start) about how to package for iOS/Android with Cordova/PhoneGap Build or Cocos2d-JS.
|
||||
|
@@ -1,7 +1,5 @@
|
||||
[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
2
newIDE/app/.gitignore
vendored
@@ -5,7 +5,6 @@ node_modules
|
||||
|
||||
# testing
|
||||
coverage
|
||||
flow-coverage
|
||||
|
||||
# production
|
||||
build
|
||||
@@ -24,4 +23,3 @@ public/res
|
||||
public/CppPlatform
|
||||
public/JsPlatform
|
||||
resources/GDJS
|
||||
|
||||
|
18
newIDE/app/flow-typed/libGD.js
vendored
18
newIDE/app/flow-typed/libGD.js
vendored
@@ -1,18 +0,0 @@
|
||||
// @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;
|
@@ -1,94 +0,0 @@
|
||||
// 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,74 +5,62 @@
|
||||
"license": "MIT",
|
||||
"homepage": ".",
|
||||
"devDependencies": {
|
||||
"flow-bin": "^0.61.0",
|
||||
"flow-coverage-report": "^0.4.0",
|
||||
"flow-bin": "^0.58.0",
|
||||
"follow-redirects": "^1.2.3",
|
||||
"prettier": "1.7.0",
|
||||
"react-scripts": "1.0.17",
|
||||
"react-scripts": "1.0.6",
|
||||
"shelljs": "^0.7.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/react": "3.2.19",
|
||||
"@storybook/react": "3.2.14",
|
||||
"aws-sdk": "^2.100.0",
|
||||
"axios": "^0.16.1",
|
||||
"blueimp-md5": "^2.10.0",
|
||||
"classnames": "2.2.5",
|
||||
"date-fns": "^1.29.0",
|
||||
"element-closest": "2.0.2",
|
||||
"firebase": "^4.8.2",
|
||||
"classnames": "^2.2.5",
|
||||
"cursores": "^1.0.1",
|
||||
"element-closest": "^2.0.2",
|
||||
"flat": "2.0.1",
|
||||
"fontfaceobserver": "2.0.13",
|
||||
"fontfaceobserver": "^2.0.13",
|
||||
"i18next": "^10.0.3",
|
||||
"keen-tracking": "1.1.3",
|
||||
"lodash": "4.17.4",
|
||||
"material-ui": "0.20",
|
||||
"material-ui-search-bar": "0.4.1",
|
||||
"material-ui": "0.18.3",
|
||||
"material-ui-search-bar": "0.4.0",
|
||||
"pixi-simple-gesture": "0.2.2",
|
||||
"pixi.js": "3.0.11",
|
||||
"prop-types": "^15.5.10",
|
||||
"raven-js": "^3.19.1",
|
||||
"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": "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": "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",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"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.*"
|
||||
"build-storybook": "build-storybook -s public"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"flow-coverage-report": {
|
||||
"includeGlob": [
|
||||
"src/**/*.js"
|
||||
],
|
||||
"type": [
|
||||
"text",
|
||||
"html",
|
||||
"json"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
<title>GDevelop 5</title>
|
||||
|
||||
@@ -43,10 +43,6 @@
|
||||
';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-->
|
||||
|
File diff suppressed because one or more lines are too long
@@ -65,12 +65,7 @@ export default class NewBehaviorDialog extends Component {
|
||||
if (!open || !project) return null;
|
||||
|
||||
const actions = [
|
||||
<FlatButton
|
||||
key="close"
|
||||
label="Close"
|
||||
primary={false}
|
||||
onClick={onClose}
|
||||
/>,
|
||||
<FlatButton key="close" label="Close" primary={false} onClick={onClose} />,
|
||||
];
|
||||
|
||||
return (
|
||||
|
10
newIDE/app/src/EventsSheet/ClassNames.js
Normal file
10
newIDE/app/src/EventsSheet/ClassNames.js
Normal file
@@ -0,0 +1,10 @@
|
||||
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';
|
@@ -1,16 +0,0 @@
|
||||
// @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;
|
@@ -6,7 +6,6 @@ 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: {
|
||||
@@ -18,7 +17,6 @@ export default {
|
||||
'BuiltinCommonInstructions::Repeat': RepeatEvent,
|
||||
'BuiltinCommonInstructions::While': WhileEvent,
|
||||
'BuiltinCommonInstructions::Link': LinkEvent,
|
||||
'BuiltinCommonInstructions::JsCode': JsCodeEvent,
|
||||
},
|
||||
getEventComponent: function(event) {
|
||||
if (this.components.hasOwnProperty(event.getType()))
|
@@ -1,15 +0,0 @@
|
||||
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';
|
@@ -1,50 +0,0 @@
|
||||
// @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;
|
||||
}
|
||||
}
|
@@ -1,192 +0,0 @@
|
||||
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,19 +1,60 @@
|
||||
import React, { Component } from 'react';
|
||||
import muiThemeable from 'material-ui/styles/muiThemeable';
|
||||
import findIndex from 'lodash/findIndex';
|
||||
import {
|
||||
SortableTreeWithoutDndContext,
|
||||
SortableTreeWithoutDndContext as SortableTree,
|
||||
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
|
||||
@@ -66,8 +107,6 @@ class EventContainer extends Component {
|
||||
this.props.onInstructionsListContextMenu
|
||||
}
|
||||
onParameterClick={this.props.onParameterClick}
|
||||
onOpenExternalEvents={this.props.onOpenExternalEvents}
|
||||
onOpenLayout={this.props.onOpenLayout}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -77,19 +116,11 @@ 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 ThemableEventsTree extends Component {
|
||||
export default class EventsTree extends Component {
|
||||
static defaultProps = {
|
||||
selection: getInitialSelection(),
|
||||
};
|
||||
@@ -112,7 +143,7 @@ export default class ThemableEventsTree extends Component {
|
||||
*/
|
||||
onHeightsChanged(cb) {
|
||||
this.forceUpdate(() => {
|
||||
if (this._list) this._list.wrappedInstance.recomputeRowHeights();
|
||||
this._list.wrappedInstance.recomputeRowHeights();
|
||||
if (cb) cb();
|
||||
});
|
||||
}
|
||||
@@ -123,14 +154,14 @@ export default class ThemableEventsTree extends Component {
|
||||
*/
|
||||
forceEventsUpdate(cb) {
|
||||
this.setState(this._eventsToTreeData(this.props.events), () => {
|
||||
if (this._list) this._list.wrappedInstance.recomputeRowHeights();
|
||||
this._list.wrappedInstance.recomputeRowHeights();
|
||||
if (cb) cb();
|
||||
});
|
||||
}
|
||||
|
||||
scrollToEvent(event) {
|
||||
const row = this._getEventRow(event);
|
||||
if (row !== -1 && this._list) this._list.wrappedInstance.scrollToRow(row);
|
||||
if (row !== -1) this._list.wrappedInstance.scrollToRow(row);
|
||||
}
|
||||
|
||||
_getEventRow(searchedEvent) {
|
||||
@@ -231,18 +262,15 @@ export default class ThemableEventsTree 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: height || 400 }}>
|
||||
<div style={{ height: this.props.height || 400 }}>
|
||||
<SortableTree
|
||||
className={eventsTree}
|
||||
treeData={this.state.treeData}
|
||||
scaffoldBlockPxWidth={indentWidth}
|
||||
onChange={() => {}}
|
||||
@@ -251,8 +279,6 @@ export default class ThemableEventsTree extends Component {
|
||||
canDrop={this._canDrop}
|
||||
rowHeight={({ index }) => {
|
||||
const event = this.state.flatData[index];
|
||||
if (!event) return 0;
|
||||
|
||||
return this.eventsHeightsCache.getEventHeight(event);
|
||||
}}
|
||||
reactVirtualizedListProps={{
|
||||
|
@@ -4,10 +4,14 @@ import ParameterRenderingService from './InstructionEditor/ParameterRenderingSer
|
||||
const gd = global.gd;
|
||||
|
||||
export default class InlineParameterEditor extends Component {
|
||||
state = {
|
||||
isValid: false,
|
||||
parameterMetadata: null,
|
||||
};
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isValid: false,
|
||||
parameterMetadata: null,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps) {
|
||||
if (
|
||||
|
@@ -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, subInstructionsContainer, instructionParameter } from './ClassNames';
|
||||
import { selectedArea, selectableArea } from './ClassNames';
|
||||
import InstructionsList from './InstructionsList';
|
||||
const gd = global.gd;
|
||||
const instrFormatter = gd.InstructionSentenceFormatter.get();
|
||||
@@ -20,6 +20,12 @@ const styles = {
|
||||
paddingLeft: 2,
|
||||
paddingRight: 2,
|
||||
},
|
||||
subInstructionsList: {
|
||||
marginLeft: 9,
|
||||
marginTop: 1,
|
||||
borderRight: 'none',
|
||||
borderLeft: '1px solid #d3d3d3',
|
||||
},
|
||||
};
|
||||
|
||||
export default class Instruction extends Component {
|
||||
@@ -55,20 +61,29 @@ 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)}
|
||||
@@ -134,7 +149,7 @@ export default class Instruction extends Component {
|
||||
{this._renderInstructionText(metadata)}
|
||||
{metadata.canHaveSubInstructions() && (
|
||||
<InstructionsList
|
||||
extraClassName={subInstructionsContainer}
|
||||
style={styles.subInstructionsList}
|
||||
instrsList={instruction.getSubInstructions()}
|
||||
areConditions={this.props.isCondition}
|
||||
selection={this.props.selection}
|
@@ -30,13 +30,13 @@ export default class InstructionEditorDialog extends React.Component {
|
||||
<FlatButton
|
||||
label="Cancel"
|
||||
primary={false}
|
||||
onClick={this.props.onCancel}
|
||||
onTouchTap={this.props.onCancel}
|
||||
/>,
|
||||
<FlatButton
|
||||
label="Ok"
|
||||
primary={true}
|
||||
keyboardFocused={false}
|
||||
onClick={this.props.onSubmit}
|
||||
onTouchTap={this.props.onSubmit}
|
||||
/>,
|
||||
];
|
||||
|
||||
|
@@ -15,21 +15,19 @@ 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,
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
allExpressions.forEach((expressionInfo: InstructionOrExpressionInformation) => {
|
||||
update(
|
||||
tree,
|
||||
compact(expressionInfo.fullGroupName.split(GROUP_DELIMITER)),
|
||||
groupInfo => {
|
||||
const existingGroupInfo = groupInfo || {};
|
||||
return {
|
||||
...existingGroupInfo,
|
||||
[expressionInfo.displayedName]: expressionInfo,
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return tree;
|
||||
};
|
||||
|
@@ -50,7 +50,7 @@ const enumerateExtensionExpressions = (
|
||||
return allExpressions;
|
||||
};
|
||||
|
||||
export const enumerateExpressions = type => {
|
||||
export const enumerateExpressions = (type) => {
|
||||
const freeExpressions = [];
|
||||
const objectsExpressions = [];
|
||||
const behaviorsExpressions = [];
|
||||
@@ -85,10 +85,7 @@ export const enumerateExpressions = type => {
|
||||
//Free expressions
|
||||
freeExpressions.push.apply(
|
||||
freeExpressions,
|
||||
enumerateExtensionExpressions(
|
||||
prefix,
|
||||
allFreeExpressionsGetter.call(extension)
|
||||
)
|
||||
enumerateExtensionExpressions(prefix, allFreeExpressionsGetter.call(extension))
|
||||
);
|
||||
|
||||
//Objects expressions:
|
||||
@@ -137,13 +134,14 @@ export const enumerateExpressions = type => {
|
||||
};
|
||||
|
||||
export const filterExpressions = (list, searchText) => {
|
||||
if (!searchText) return list;
|
||||
const lowercaseSearchText = searchText.toLowerCase();
|
||||
if (!searchText) return list;
|
||||
const lowercaseSearchText = searchText.toLowerCase();
|
||||
|
||||
return list.filter(enumeratedExpression => {
|
||||
return (
|
||||
enumeratedExpression.type.toLowerCase().indexOf(lowercaseSearchText) !==
|
||||
-1
|
||||
);
|
||||
});
|
||||
};
|
||||
return list.filter(enumeratedExpression => {
|
||||
return (
|
||||
enumeratedExpression.type
|
||||
.toLowerCase()
|
||||
.indexOf(lowercaseSearchText) !== -1
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@@ -15,5 +15,5 @@ describe('EnumerateInstructions', () => {
|
||||
it('can create the tree of instructions', () => {
|
||||
const instructions = enumerateInstructions('number');
|
||||
expect(createTree(instructions)).toMatchSnapshot();
|
||||
});
|
||||
})
|
||||
});
|
||||
|
@@ -1,13 +1,14 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import { enumerateExpressions } from './EnumerateExpressions';
|
||||
import InstructionOrExpressionSelector from './index';
|
||||
import { InstructionOrExpressionSelector } from './index';
|
||||
import { createTree, type InstructionOrExpressionTreeNode } from './CreateTree';
|
||||
import { type InstructionOrExpressionInformation } from './InstructionOrExpressionInformation.flow.js';
|
||||
|
||||
export default class ExpressionSelector extends Component<*, *> {
|
||||
_selector: any = null;
|
||||
instructionsInfo: Array<InstructionOrExpressionInformation> = [];
|
||||
instructionsInfoTree: ?InstructionOrExpressionTreeNode = null;
|
||||
instructionsInfoTree: InstructionOrExpressionTreeNode = null;
|
||||
|
||||
static defaultProps = {
|
||||
expressionType: 'number',
|
||||
@@ -19,9 +20,14 @@ export default class ExpressionSelector extends Component<*, *> {
|
||||
this.instructionsInfoTree = createTree(allExpressions);
|
||||
}
|
||||
|
||||
focus = () => {
|
||||
if (this._selector) this._selector.focus();
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<InstructionOrExpressionSelector
|
||||
ref={selector => (this._selector = selector)}
|
||||
instructionsInfo={this.instructionsInfo}
|
||||
instructionsInfoTree={this.instructionsInfoTree}
|
||||
{...this.props}
|
||||
|
@@ -1,5 +1,3 @@
|
||||
//@flow
|
||||
|
||||
export type InstructionOrExpressionInformation = {
|
||||
type: string,
|
||||
name?: string, //For expressions
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import InstructionOrExpressionSelector from './index';
|
||||
import { InstructionOrExpressionSelector } from './index';
|
||||
import { createTree, type InstructionOrExpressionTreeNode } from './CreateTree';
|
||||
import { enumerateInstructions } from './EnumerateInstructions';
|
||||
import { type InstructionOrExpressionInformation } from './InstructionOrExpressionInformation.flow.js';
|
||||
@@ -11,8 +11,9 @@ type Props = {
|
||||
};
|
||||
|
||||
export default class InstructionSelector extends Component<Props, *> {
|
||||
_selector: any = null;
|
||||
instructionsInfo: Array<InstructionOrExpressionInformation> = [];
|
||||
instructionsInfoTree: ?InstructionOrExpressionTreeNode = null;
|
||||
instructionsInfoTree: InstructionOrExpressionTreeNode = null;
|
||||
|
||||
componentWillMount() {
|
||||
const allInstructions = enumerateInstructions(this.props.isCondition);
|
||||
@@ -20,9 +21,14 @@ export default class InstructionSelector extends Component<Props, *> {
|
||||
this.instructionsInfoTree = createTree(allInstructions);
|
||||
}
|
||||
|
||||
focus = () => {
|
||||
if (this._selector) this._selector.focus();
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<InstructionOrExpressionSelector
|
||||
ref={selector => (this._selector = selector)}
|
||||
instructionsInfo={this.instructionsInfo}
|
||||
instructionsInfoTree={this.instructionsInfoTree}
|
||||
{...this.props}
|
||||
|
@@ -133,11 +133,6 @@ Object {
|
||||
"fullGroupName": "Common conditions for all objects/Collision",
|
||||
"type": "CollisionNP",
|
||||
},
|
||||
"Point inside object": Object {
|
||||
"displayedName": "Point inside object",
|
||||
"fullGroupName": "Common conditions for all objects/Collision",
|
||||
"type": "CollisionPoint",
|
||||
},
|
||||
},
|
||||
"Layer": Object {
|
||||
"Compare layer": Object {
|
||||
@@ -1768,11 +1763,6 @@ Array [
|
||||
"fullGroupName": "Common conditions for all objects/Behaviors",
|
||||
"type": "BehaviorActivated",
|
||||
},
|
||||
Object {
|
||||
"displayedName": "Point inside object",
|
||||
"fullGroupName": "Common conditions for all objects/Collision",
|
||||
"type": "CollisionPoint",
|
||||
},
|
||||
Object {
|
||||
"displayedName": "Compare layer",
|
||||
"fullGroupName": "Common conditions for all objects/Layer",
|
||||
|
@@ -2,7 +2,6 @@ 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 = {
|
||||
@@ -10,12 +9,18 @@ const styles = {
|
||||
margin: '0 auto',
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
groupListItemText: {
|
||||
color: 'rgba(0,0,0,0.54)',
|
||||
},
|
||||
groupListItem: {
|
||||
borderBottom: '1px solid #f0f0f0', //TODO: Use theme color instead
|
||||
},
|
||||
groupListItemNestedList: {
|
||||
padding: 0,
|
||||
},
|
||||
};
|
||||
|
||||
class ThemableInstructionOrExpressionSelector extends Component {
|
||||
export class InstructionOrExpressionSelector extends Component {
|
||||
state = {
|
||||
search: '',
|
||||
searchResults: [],
|
||||
@@ -56,8 +61,6 @@ class ThemableInstructionOrExpressionSelector extends Component {
|
||||
};
|
||||
|
||||
_renderTree(instructionInfoTree) {
|
||||
const { muiTheme } = this.props;
|
||||
|
||||
return Object.keys(instructionInfoTree).map(key => {
|
||||
const instructionOrGroup = instructionInfoTree[key];
|
||||
|
||||
@@ -76,9 +79,9 @@ class ThemableInstructionOrExpressionSelector extends Component {
|
||||
return (
|
||||
<ListItem
|
||||
key={key}
|
||||
style={{borderBottom: `1px solid ${muiTheme.listItem.separatorColor}`}}
|
||||
style={styles.groupListItem}
|
||||
nestedListStyle={styles.groupListItemNestedList}
|
||||
primaryText={<div style={{color: muiTheme.listItem.groupTextColor}}>{key}</div>}
|
||||
primaryText={<div style={styles.groupListItemText}>{key}</div>}
|
||||
primaryTogglesNestedList={true}
|
||||
autoGenerateNestedIndicator={true}
|
||||
nestedItems={this._renderTree(instructionOrGroup)}
|
||||
@@ -106,15 +109,8 @@ class ThemableInstructionOrExpressionSelector extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { muiTheme, selectedType, instructionsInfoTree, style } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: muiTheme.list.itemsBackgroundColor,
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
<div style={this.props.style}>
|
||||
<SearchBar
|
||||
onChange={text =>
|
||||
this.setState({
|
||||
@@ -125,17 +121,16 @@ class ThemableInstructionOrExpressionSelector extends Component {
|
||||
style={styles.searchBar}
|
||||
ref={searchBar => (this._searchBar = searchBar)}
|
||||
/>
|
||||
<SelectableList value={selectedType}>
|
||||
<SelectableList
|
||||
value={this.props.selectedType}
|
||||
>
|
||||
{this.state.search
|
||||
? this._renderSearchResults()
|
||||
: this._renderTree(instructionsInfoTree)}
|
||||
: this._renderTree(this.props.instructionsInfoTree)}
|
||||
</SelectableList>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const InstructionOrExpressionSelector = muiThemeable()(
|
||||
ThemableInstructionOrExpressionSelector
|
||||
);
|
||||
export default InstructionOrExpressionSelector;
|
||||
|
@@ -29,20 +29,15 @@ export default class ExpressionParametersEditorDialog extends Component<
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
this.setState({
|
||||
this.state = {
|
||||
parameterValues: Array(
|
||||
this.props.expressionMetadata.getParametersCount()
|
||||
).fill(''),
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
project,
|
||||
layout,
|
||||
expressionMetadata,
|
||||
parameterRenderingService,
|
||||
} = this.props;
|
||||
const { project, layout, expressionMetadata, parameterRenderingService } = this.props;
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
|
@@ -20,8 +20,6 @@ export const formatExpressionCall = (
|
||||
expressionInfo: InstructionOrExpressionInformation,
|
||||
parameterValues: ParameterValues
|
||||
): string => {
|
||||
const functionName = expressionInfo.name || '';
|
||||
|
||||
if (expressionInfo.objectMetadata) {
|
||||
const [objectName, ...otherParameters] = parameterValues;
|
||||
|
||||
@@ -29,7 +27,7 @@ export const formatExpressionCall = (
|
||||
otherParameters,
|
||||
expressionInfo.metadata
|
||||
).join(', ');
|
||||
return `${objectName}.${functionName}(${functionArgs})`;
|
||||
return `${objectName}.${expressionInfo.name}(${functionArgs})`;
|
||||
} else if (expressionInfo.behaviorMetadata) {
|
||||
const [objectName, behaviorName, ...otherParameters] = parameterValues;
|
||||
|
||||
@@ -37,12 +35,12 @@ export const formatExpressionCall = (
|
||||
otherParameters,
|
||||
expressionInfo.metadata
|
||||
).join(', ');
|
||||
return `${objectName}.${behaviorName}::${functionName}(${functionArgs})`;
|
||||
return `${objectName}.${behaviorName}::${expressionInfo.name}(${functionArgs})`;
|
||||
} else {
|
||||
const functionArgs = filterOutCodeOnlyParameters(
|
||||
parameterValues,
|
||||
expressionInfo.metadata
|
||||
).join(', ');
|
||||
return `${functionName}(${functionArgs})`;
|
||||
return `${expressionInfo.name}(${functionArgs})`;
|
||||
}
|
||||
};
|
||||
|
@@ -25,13 +25,10 @@ describe('HelpButton', () => {
|
||||
});
|
||||
|
||||
it('properly format a free function, with "code-only" parameters', () => {
|
||||
const cameraHeightExpression = filterExpressions(
|
||||
freeExpressions,
|
||||
'CameraHeight'
|
||||
)[0];
|
||||
expect(
|
||||
formatExpressionCall(cameraHeightExpression, ['', '"My layer"', '0'])
|
||||
).toBe('CameraHeight("My layer", 0)');
|
||||
const cameraHeightExpression = filterExpressions(freeExpressions, 'CameraHeight')[0];
|
||||
expect(formatExpressionCall(cameraHeightExpression, ['', '"My layer"', "0"])).toBe(
|
||||
'CameraHeight("My layer", 0)'
|
||||
);
|
||||
});
|
||||
|
||||
it('properly format an object function', () => {
|
||||
|
@@ -20,8 +20,9 @@ const styles = {
|
||||
flex: 1,
|
||||
},
|
||||
expressionSelector: {
|
||||
maxHeight: 350,
|
||||
maxHeight: 250,
|
||||
overflowY: 'scroll',
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
input: {
|
||||
fontFamily: '"Lucida Console", Monaco, monospace',
|
||||
|
@@ -11,6 +11,7 @@ const styles = {
|
||||
typeSelector: {
|
||||
flex: 1,
|
||||
overflowY: 'scroll',
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
parametersEditor: {
|
||||
flex: 2,
|
||||
@@ -22,6 +23,10 @@ const styles = {
|
||||
};
|
||||
|
||||
export default class InstructionEditor extends Component {
|
||||
componentDidMount() {
|
||||
if (this._typeSelector) this._typeSelector.focus();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { instruction, isCondition, project, layout } = this.props;
|
||||
|
||||
@@ -35,6 +40,7 @@ export default class InstructionEditor extends Component {
|
||||
instruction.setType(type);
|
||||
this.forceUpdate();
|
||||
}}
|
||||
ref={typeSelector => (this._typeSelector = typeSelector)}
|
||||
/>
|
||||
<Paper style={styles.parametersEditor} rounded={false} zDepth={2}>
|
||||
<InstructionParametersEditor
|
||||
|
@@ -1,11 +1,20 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Instruction from './Instruction';
|
||||
import { mapFor } from '../../Utils/MapFor';
|
||||
import { isInstructionSelected } from '../SelectionHandler';
|
||||
import { actionsContainer, conditionsContainer } from './ClassNames';
|
||||
import Instruction from './Instruction.js';
|
||||
import { mapFor } from '../Utils/MapFor';
|
||||
import { isInstructionSelected } from './SelectionHandler';
|
||||
|
||||
const styles = {
|
||||
conditionsContainer: {
|
||||
paddingLeft: 5,
|
||||
paddingRight: 5,
|
||||
background: '#f1f2f2',
|
||||
borderRight: '1px solid #d3d3d3',
|
||||
},
|
||||
actionsContainer: {
|
||||
paddingLeft: 5,
|
||||
paddingRight: 5,
|
||||
},
|
||||
addButton: {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
@@ -34,26 +43,16 @@ export default class InstructionsList extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
addButtonLabel,
|
||||
areConditions,
|
||||
extraClassName,
|
||||
instrsList,
|
||||
onAddNewInstruction,
|
||||
onInstructionClick,
|
||||
onInstructionContextMenu,
|
||||
onInstructionDoubleClick,
|
||||
onInstructionsListContextMenu,
|
||||
onParameterClick,
|
||||
selection,
|
||||
style,
|
||||
} = this.props;
|
||||
const instructionsListContext = {
|
||||
isCondition: this.props.areConditions,
|
||||
instrsList: this.props.instrsList,
|
||||
};
|
||||
|
||||
const instructions = mapFor(0, instrsList.size(), i => {
|
||||
const instruction = instrsList.get(i);
|
||||
const instructions = mapFor(0, this.props.instrsList.size(), i => {
|
||||
const instruction = this.props.instrsList.get(i);
|
||||
const instructionContext = {
|
||||
isCondition: areConditions,
|
||||
instrsList: instrsList,
|
||||
isCondition: this.props.areConditions,
|
||||
instrsList: this.props.instrsList,
|
||||
instruction,
|
||||
indexInList: i,
|
||||
};
|
||||
@@ -61,49 +60,44 @@ export default class InstructionsList extends Component {
|
||||
return (
|
||||
<Instruction
|
||||
instruction={instruction}
|
||||
isCondition={areConditions}
|
||||
instrsList={instrsList}
|
||||
isCondition={this.props.areConditions}
|
||||
instrsList={this.props.instrsList}
|
||||
index={i}
|
||||
key={instruction.ptr}
|
||||
selected={isInstructionSelected(selection, instruction)}
|
||||
onClick={() => onInstructionClick(instructionContext)}
|
||||
selected={isInstructionSelected(this.props.selection, instruction)}
|
||||
onClick={() => this.props.onInstructionClick(instructionContext)}
|
||||
onDoubleClick={() =>
|
||||
onInstructionDoubleClick(instructionContext)}
|
||||
this.props.onInstructionDoubleClick(instructionContext)}
|
||||
onContextMenu={(x, y) =>
|
||||
onInstructionContextMenu(x, y, instructionContext)}
|
||||
this.props.onInstructionContextMenu(x, y, instructionContext)}
|
||||
onParameterClick={(domEvent, parameterIndex) =>
|
||||
onParameterClick({
|
||||
this.props.onParameterClick({
|
||||
...instructionContext,
|
||||
parameterIndex,
|
||||
domEvent,
|
||||
})}
|
||||
selection={selection}
|
||||
onAddNewSubInstruction={onAddNewInstruction}
|
||||
onSubInstructionClick={onInstructionClick}
|
||||
onSubInstructionDoubleClick={onInstructionDoubleClick}
|
||||
onSubInstructionContextMenu={onInstructionContextMenu}
|
||||
selection={this.props.selection}
|
||||
onAddNewSubInstruction={this.props.onAddNewInstruction}
|
||||
onSubInstructionClick={this.props.onInstructionClick}
|
||||
onSubInstructionDoubleClick={this.props.onInstructionDoubleClick}
|
||||
onSubInstructionContextMenu={this.props.onInstructionContextMenu}
|
||||
onSubInstructionsListContextMenu={
|
||||
onInstructionsListContextMenu
|
||||
this.props.onInstructionsListContextMenu
|
||||
}
|
||||
onSubParameterClick={onParameterClick}
|
||||
onSubParameterClick={this.props.onParameterClick}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const instructionsListContext = {
|
||||
isCondition: areConditions,
|
||||
instrsList: instrsList,
|
||||
};
|
||||
const addButtonDefaultLabel = areConditions
|
||||
const containerStyle = this.props.areConditions
|
||||
? styles.conditionsContainer
|
||||
: styles.actionsContainer;
|
||||
|
||||
const addButtonLabel = this.props.areConditions
|
||||
? 'Add condition'
|
||||
: 'Add action';
|
||||
return (
|
||||
<div
|
||||
className={`${areConditions
|
||||
? conditionsContainer
|
||||
: actionsContainer} ${extraClassName || ''}`}
|
||||
style={style}
|
||||
>
|
||||
<div style={{ ...containerStyle, ...this.props.style }}>
|
||||
{instructions}
|
||||
<a
|
||||
style={styles.addButton}
|
||||
@@ -111,14 +105,14 @@ export default class InstructionsList extends Component {
|
||||
onClick={this.onAddNewInstruction}
|
||||
onContextMenu={e => {
|
||||
e.stopPropagation();
|
||||
onInstructionsListContextMenu(
|
||||
this.props.onInstructionsListContextMenu(
|
||||
e.clientX,
|
||||
e.clientY,
|
||||
instructionsListContext
|
||||
);
|
||||
}}
|
||||
>
|
||||
{addButtonLabel || addButtonDefaultLabel}
|
||||
{this.props.addButtonLabel || addButtonLabel}
|
||||
</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,9 +37,13 @@ export default class CommentEvent extends Component {
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
editing: false,
|
||||
};
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.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,7 +75,6 @@ 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: 40,
|
||||
height: 60,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
padding: 5,
|
@@ -1,16 +1,13 @@
|
||||
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 ExternalEventsField from '../../InstructionEditor/ParameterFields/ExternalEventsField';
|
||||
import { showWarningBox } from '../../../UI/Messages/MessageBox';
|
||||
import InlinePopover from '../InlinePopover';
|
||||
import ExternalEventsField from '../InstructionEditor/ParameterFields/ExternalEventsField';
|
||||
const gd = global.gd;
|
||||
|
||||
const styles = {
|
||||
@@ -42,31 +39,12 @@ export default class LinkEvent extends Component {
|
||||
}
|
||||
|
||||
edit = domEvent => {
|
||||
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.'
|
||||
);
|
||||
}
|
||||
this.setState({
|
||||
editing: true,
|
||||
anchorEl: domEvent.currentTarget,
|
||||
}, () => {
|
||||
if (this._externalEventsField) this._externalEventsField.focus();
|
||||
});
|
||||
};
|
||||
|
||||
endEditing = () => {
|
||||
@@ -77,7 +55,7 @@ export default class LinkEvent extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const linkEvent = gd.asLinkEvent(this.props.event);
|
||||
var linkEvent = gd.asLinkEvent(this.props.event);
|
||||
const target = linkEvent.getTarget();
|
||||
|
||||
return (
|
||||
@@ -99,9 +77,6 @@ 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}
|
||||
@@ -115,8 +90,7 @@ export default class LinkEvent extends Component {
|
||||
this.props.onUpdate();
|
||||
}}
|
||||
isInline
|
||||
ref={externalEventsField =>
|
||||
(this._externalEventsField = externalEventsField)}
|
||||
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,7 +75,6 @@ export default class RepeatEvent extends Component {
|
||||
className={classNames({
|
||||
[largeSelectableArea]: true,
|
||||
[largeSelectedArea]: this.props.selected,
|
||||
[executableEventContainer]: true,
|
||||
})}
|
||||
>
|
||||
<div
|
@@ -1,17 +1,14 @@
|
||||
import React, { Component } from 'react';
|
||||
import InstructionsList from '../InstructionsList';
|
||||
import InstructionsList from '../InstructionsList.js';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
largeSelectedArea,
|
||||
largeSelectableArea,
|
||||
executableEventContainer,
|
||||
} from '../ClassNames';
|
||||
import { largeSelectedArea, largeSelectableArea } from '../ClassNames';
|
||||
const gd = global.gd;
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
display: 'flex',
|
||||
borderBottom: '1px solid #d3d3d3',
|
||||
},
|
||||
actionsList: {
|
||||
flex: 1,
|
||||
@@ -43,7 +40,6 @@ export default class StandardEvent extends Component {
|
||||
className={classNames({
|
||||
[largeSelectableArea]: true,
|
||||
[largeSelectedArea]: this.props.selected,
|
||||
[executableEventContainer]: true,
|
||||
})}
|
||||
>
|
||||
<InstructionsList
|
@@ -1,18 +1,15 @@
|
||||
import React, { Component } from 'react';
|
||||
import InstructionsList from '../InstructionsList';
|
||||
import InstructionsList from '../InstructionsList.js';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
largeSelectedArea,
|
||||
largeSelectableArea,
|
||||
executableEventContainer,
|
||||
} from '../ClassNames';
|
||||
import { largeSelectedArea, largeSelectableArea } from '../ClassNames';
|
||||
const gd = global.gd;
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
borderBottom: '1px solid #d3d3d3',
|
||||
},
|
||||
instructionsContainer: {
|
||||
display: 'flex',
|
||||
@@ -48,7 +45,6 @@ export default class ForEachEvent extends Component {
|
||||
className={classNames({
|
||||
[largeSelectableArea]: true,
|
||||
[largeSelectedArea]: this.props.selected,
|
||||
[executableEventContainer]: true,
|
||||
})}
|
||||
>
|
||||
<div>While these conditions are true:</div>
|
@@ -70,13 +70,12 @@ export class Toolbar extends PureComponent {
|
||||
tooltip={t('Choose and add an event')}
|
||||
/>
|
||||
}
|
||||
buildMenuTemplate={() =>
|
||||
this.allEventsMetadata.map(metadata => {
|
||||
return {
|
||||
label: metadata.fullName,
|
||||
click: () => this.props.onAddEvent(metadata.type),
|
||||
};
|
||||
})}
|
||||
menuTemplate={this.allEventsMetadata.map(metadata => {
|
||||
return {
|
||||
label: metadata.fullName,
|
||||
click: () => this.props.onAddEvent(metadata.type),
|
||||
};
|
||||
})}
|
||||
/>
|
||||
<ToolbarSeparator />
|
||||
<ToolbarIcon
|
||||
|
@@ -1,6 +1,8 @@
|
||||
import React, { Component } from 'react';
|
||||
import EventsTree from './EventsTree';
|
||||
import InstructionEditorDialog from './InstructionEditor/InstructionEditorDialog';
|
||||
import '../UI/Theme/EventsSheet.css';
|
||||
import { container } from './ClassNames';
|
||||
import Toolbar from './Toolbar';
|
||||
import KeyboardShortcuts from '../UI/KeyboardShortcuts';
|
||||
import { passFullSize } from '../UI/FullSizeMeasurer';
|
||||
@@ -35,7 +37,6 @@ import {
|
||||
getSelectedInstructionsListsContexts,
|
||||
selectInstructionsList,
|
||||
} from './SelectionHandler';
|
||||
import EmptyEventsPlaceholder from './EmptyEventsPlaceholder';
|
||||
const gd = global.gd;
|
||||
|
||||
const CLIPBOARD_KIND = 'EventsAndInstructions';
|
||||
@@ -189,20 +190,17 @@ export default class EventsSheet extends Component {
|
||||
this.state.editedInstruction.instruction.delete();
|
||||
}
|
||||
|
||||
this.setState(
|
||||
{
|
||||
editedInstruction: {
|
||||
isCondition: true,
|
||||
instruction: null,
|
||||
instrsList: null,
|
||||
},
|
||||
this.setState({
|
||||
editedInstruction: {
|
||||
isCondition: true,
|
||||
instruction: null,
|
||||
instrsList: null,
|
||||
},
|
||||
() => {
|
||||
if (saveChanges) {
|
||||
this._saveChangesToHistory();
|
||||
}
|
||||
}, () => {
|
||||
if (saveChanges) {
|
||||
this._saveChangesToHistory();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
selectEvent = eventContext => {
|
||||
@@ -456,18 +454,12 @@ export default class EventsSheet extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
project,
|
||||
layout,
|
||||
events,
|
||||
onOpenExternalEvents,
|
||||
onOpenLayout,
|
||||
} = this.props;
|
||||
const { project, layout, events } = this.props;
|
||||
if (!project) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="gd-events-sheet"
|
||||
className={container}
|
||||
onFocus={() => this._keyboardShortcuts.focus()}
|
||||
onBlur={() => this._keyboardShortcuts.blur()}
|
||||
tabIndex={1}
|
||||
@@ -489,10 +481,7 @@ export default class EventsSheet extends Component {
|
||||
onEventContextMenu={this.openEventContextMenu}
|
||||
onAddNewEvent={context =>
|
||||
this.addNewEvent('BuiltinCommonInstructions::Standard', context)}
|
||||
onOpenExternalEvents={onOpenExternalEvents}
|
||||
onOpenLayout={onOpenLayout}
|
||||
/>
|
||||
{events && events.getEventsCount() === 0 && <EmptyEventsPlaceholder />}
|
||||
<InlineParameterEditor
|
||||
open={this.state.inlineEditing}
|
||||
anchorEl={this.state.inlineEditingAnchorEl}
|
||||
@@ -510,7 +499,7 @@ export default class EventsSheet extends Component {
|
||||
/>
|
||||
<ContextMenu
|
||||
ref={eventContextMenu => (this.eventContextMenu = eventContextMenu)}
|
||||
buildMenuTemplate={() => [
|
||||
menuTemplate={[
|
||||
{
|
||||
label: 'Copy',
|
||||
click: () => this.copySelection(),
|
||||
@@ -524,7 +513,6 @@ export default class EventsSheet extends Component {
|
||||
{
|
||||
label: 'Paste',
|
||||
click: () => this.pasteEvents(),
|
||||
enabled: Clipboard.has(CLIPBOARD_KIND),
|
||||
accelerator: 'CmdOrCtrl+V',
|
||||
},
|
||||
{ type: 'separator' },
|
||||
@@ -537,13 +525,11 @@ export default class EventsSheet extends Component {
|
||||
{
|
||||
label: 'Undo',
|
||||
click: this.undo,
|
||||
enabled: canUndo(this.state.history),
|
||||
accelerator: 'CmdOrCtrl+Z',
|
||||
},
|
||||
{
|
||||
label: 'Redo',
|
||||
click: this.redo,
|
||||
enabled: canRedo(this.state.history),
|
||||
accelerator: 'CmdOrCtrl+Shift+Z',
|
||||
},
|
||||
]}
|
||||
@@ -551,7 +537,7 @@ export default class EventsSheet extends Component {
|
||||
<ContextMenu
|
||||
ref={instructionContextMenu =>
|
||||
(this.instructionContextMenu = instructionContextMenu)}
|
||||
buildMenuTemplate={() => [
|
||||
menuTemplate={[
|
||||
{
|
||||
label: 'Copy',
|
||||
click: () => this.copySelection(),
|
||||
@@ -565,7 +551,6 @@ export default class EventsSheet extends Component {
|
||||
{
|
||||
label: 'Paste',
|
||||
click: () => this.pasteInstructions(),
|
||||
enabled: Clipboard.has(CLIPBOARD_KIND),
|
||||
accelerator: 'CmdOrCtrl+V',
|
||||
},
|
||||
{ type: 'separator' },
|
||||
@@ -578,13 +563,11 @@ export default class EventsSheet extends Component {
|
||||
{
|
||||
label: 'Undo',
|
||||
click: this.undo,
|
||||
enabled: canUndo(this.state.history),
|
||||
accelerator: 'CmdOrCtrl+Z',
|
||||
},
|
||||
{
|
||||
label: 'Redo',
|
||||
click: this.redo,
|
||||
enabled: canRedo(this.state.history),
|
||||
accelerator: 'CmdOrCtrl+Shift+Z',
|
||||
},
|
||||
]}
|
||||
@@ -592,24 +575,21 @@ export default class EventsSheet extends Component {
|
||||
<ContextMenu
|
||||
ref={instructionsListContextMenu =>
|
||||
(this.instructionsListContextMenu = instructionsListContextMenu)}
|
||||
buildMenuTemplate={() => [
|
||||
menuTemplate={[
|
||||
{
|
||||
label: 'Paste',
|
||||
click: () => this.pasteInstructions(),
|
||||
enabled: Clipboard.has(CLIPBOARD_KIND),
|
||||
accelerator: 'CmdOrCtrl+V',
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Undo',
|
||||
click: this.undo,
|
||||
enabled: canUndo(this.state.history),
|
||||
accelerator: 'CmdOrCtrl+Z',
|
||||
},
|
||||
{
|
||||
label: 'Redo',
|
||||
click: this.redo,
|
||||
enabled: canRedo(this.state.history),
|
||||
accelerator: 'CmdOrCtrl+Shift+Z',
|
||||
},
|
||||
]}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
import { Column, Line } from '../../UI/Grid';
|
||||
import Window from '../../Utils/Window';
|
||||
import { Column, Line } from '../UI/Grid';
|
||||
import Window from '../Utils/Window';
|
||||
|
||||
export default class BrowserExport extends Component {
|
||||
openWebsite = () => {
|
||||
@@ -19,7 +19,7 @@ export default class BrowserExport extends Component {
|
||||
</Line>
|
||||
<Line>
|
||||
<RaisedButton
|
||||
onClick={this.openWebsite}
|
||||
onTouchTap={this.openWebsite}
|
||||
primary
|
||||
label="Download GDevelop"
|
||||
/>
|
@@ -1,60 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import BrowserExport from './BrowserExport';
|
||||
import PhoneIphone from 'material-ui/svg-icons/hardware/phone-iphone';
|
||||
import LaptopMac from 'material-ui/svg-icons/hardware/laptop-mac';
|
||||
import Folder from 'material-ui/svg-icons/file/folder';
|
||||
import Devices from 'material-ui/svg-icons/device/devices';
|
||||
|
||||
export const getBrowserExporters = () => [
|
||||
{
|
||||
name: 'Android (& iOS coming soon)',
|
||||
renderIcon: props => <PhoneIphone {...props} />,
|
||||
description:
|
||||
'Package your game for Android directly from GDevelop. iOS support is coming soon!',
|
||||
key: 'localonlinecordovaexport',
|
||||
ExportComponent: BrowserExport,
|
||||
},
|
||||
{
|
||||
name: 'Web (upload online)',
|
||||
renderIcon: props => <Devices {...props} />,
|
||||
description:
|
||||
'Upload your game online directly from GDevelop and share the link to players. Play to your game using your browser on computers and mobile phones.',
|
||||
key: 'locals3export',
|
||||
ExportComponent: BrowserExport,
|
||||
},
|
||||
{
|
||||
name: 'Local folder',
|
||||
renderIcon: props => <Folder {...props} />,
|
||||
description:
|
||||
'Build the game locally as a HTML5 game. You can then export it on website like Itch.io or Kongregate.',
|
||||
key: 'localexport',
|
||||
ExportComponent: BrowserExport,
|
||||
advanced: true,
|
||||
},
|
||||
{
|
||||
name: 'iOS & Android (manual)',
|
||||
renderIcon: props => <PhoneIphone {...props} />,
|
||||
description:
|
||||
'Build the game locally as a Cordova project, and export it manually then to iOS or Android with Cordova developers tools.',
|
||||
key: 'localcordovaexport',
|
||||
ExportComponent: BrowserExport,
|
||||
advanced: true,
|
||||
},
|
||||
{
|
||||
name: 'Windows/macOS/Linux (coming soon)',
|
||||
renderIcon: props => <LaptopMac {...props} />,
|
||||
description: 'Package your game as an app for Windows, macOs or Linux.',
|
||||
disabled: true,
|
||||
key: 'localonlineelectronexport',
|
||||
ExportComponent: BrowserExport,
|
||||
},
|
||||
{
|
||||
name: 'Cocos2d-JS',
|
||||
renderIcon: props => <PhoneIphone {...props} />,
|
||||
description:
|
||||
'Export your game using Cocos2d-JS game engine. The game can be compiled for Android or iOS if you install Cocos2d-JS developer tools.',
|
||||
key: 'localcocos2dexport',
|
||||
ExportComponent: BrowserExport,
|
||||
experimental: true,
|
||||
},
|
||||
];
|
@@ -1,8 +1,8 @@
|
||||
import React, { Component } from 'react';
|
||||
import Dialog from '../../UI/Dialog';
|
||||
import Dialog from '../UI/Dialog';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import { showErrorBox } from '../../UI/Messages/MessageBox';
|
||||
import { Column, Line } from '../../UI/Grid';
|
||||
import { showErrorBox } from '../UI/Messages/MessageBox';
|
||||
import { Column, Line } from '../UI/Grid';
|
||||
|
||||
export default class BrowserPreviewLinkDialog extends Component {
|
||||
constructor(props) {
|
||||
@@ -31,7 +31,7 @@ export default class BrowserPreviewLinkDialog extends Component {
|
||||
<FlatButton
|
||||
label="Launch the preview"
|
||||
primary
|
||||
onClick={this._onOpen}
|
||||
onTouchTap={this._onOpen}
|
||||
/>,
|
||||
];
|
||||
|
@@ -4,27 +4,32 @@ import BrowserS3FileSystem from './BrowserS3FileSystem';
|
||||
import BrowserPreviewLinkDialog from './BrowserPreviewLinkDialog';
|
||||
import { findGDJS } from './BrowserS3GDJSFinder';
|
||||
import assignIn from 'lodash/assignIn';
|
||||
import { GDevelopGamesPreview } from '../../Utils/GDevelopServices/ApiConfigs';
|
||||
const awsS3 = require('aws-sdk/clients/s3');
|
||||
const gd = global.gd;
|
||||
|
||||
const {
|
||||
destinationBucket,
|
||||
accessKeyId,
|
||||
secretAccessKey,
|
||||
region,
|
||||
destinationBucketBaseUrl,
|
||||
} = GDevelopGamesPreview;
|
||||
const awsS3 = require('aws-sdk/clients/s3');
|
||||
const destinationBucket = `gd-games-preview`;
|
||||
const accessKeyId = process.env.REACT_APP_PREVIEW_S3_ACCESS_KEY_ID;
|
||||
const secretAccessKey = process.env.REACT_APP_PREVIEW_S3_SECRET_ACCESS_KEY;
|
||||
const region = 'eu-west-1';
|
||||
const destinationBucketBaseUrl = `https://s3-${region}.amazonaws.com/${destinationBucket}/`;
|
||||
|
||||
if (!accessKeyId || !secretAccessKey) {
|
||||
console.warn(
|
||||
"Either REACT_APP_PREVIEW_S3_ACCESS_KEY_ID or REACT_APP_PREVIEW_S3_SECRET_ACCESS_KEY are not defined. Preview in browsers won't be working"
|
||||
);
|
||||
console.info(
|
||||
'Copy .env.dist file to .env and fill the values to fix this warning.'
|
||||
);
|
||||
}
|
||||
|
||||
const awsS3Client = new awsS3({
|
||||
accessKeyId: accessKeyId,
|
||||
secretAccessKey: secretAccessKey,
|
||||
region: region,
|
||||
correctClockSkew: true,
|
||||
});
|
||||
|
||||
export default class BrowserS3PreviewLauncher {
|
||||
static _openPreviewWindow = (project: gdProject, url: string): any => {
|
||||
static _openPreviewWindow = (project, url): any => {
|
||||
const windowObjectReference = window.open(url, `_blank`);
|
||||
return {
|
||||
url,
|
||||
@@ -68,10 +73,7 @@ export default class BrowserS3PreviewLauncher {
|
||||
});
|
||||
};
|
||||
|
||||
static launchLayoutPreview = (
|
||||
project: gdProject,
|
||||
layout: gdLayout
|
||||
): Promise<any> => {
|
||||
static launchLayoutPreview = (project, layout): Promise<any> => {
|
||||
if (!project || !layout) return Promise.reject();
|
||||
|
||||
return BrowserS3PreviewLauncher._prepareExporter().then(
|
||||
@@ -97,9 +99,9 @@ export default class BrowserS3PreviewLauncher {
|
||||
};
|
||||
|
||||
static launchExternalLayoutPreview = (
|
||||
project: gdProject,
|
||||
layout: gdLayout,
|
||||
externalLayout: gdExternalLayout
|
||||
project,
|
||||
layout,
|
||||
externalLayout
|
||||
): Promise<any> => {
|
||||
return Promise.reject('Not implemented');
|
||||
};
|
@@ -2,26 +2,28 @@ import React, { Component } from 'react';
|
||||
import Dialog from '../UI/Dialog';
|
||||
import HelpButton from '../UI/HelpButton';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import Subheader from 'material-ui/Subheader';
|
||||
import { List, ListItem } from 'material-ui/List';
|
||||
import { Tabs, Tab } from 'material-ui/Tabs';
|
||||
import Visibility from 'material-ui/svg-icons/action/visibility';
|
||||
import VisibilityOff from 'material-ui/svg-icons/action/visibility-off';
|
||||
|
||||
const styles = {
|
||||
icon: { width: 40, height: 40 },
|
||||
disabledItem: { opacity: 0.6 },
|
||||
content: { padding: 24 },
|
||||
content: {
|
||||
padding: 0,
|
||||
},
|
||||
tabContent: {
|
||||
padding: 24,
|
||||
},
|
||||
};
|
||||
|
||||
export default class ExportDialog extends Component {
|
||||
state = {
|
||||
chosenExporterKey: '',
|
||||
value: 0,
|
||||
showExperimental: false,
|
||||
};
|
||||
|
||||
chooseExporter = key => {
|
||||
_onChangeTab = value => {
|
||||
this.setState({
|
||||
chosenExporterKey: key,
|
||||
value,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -31,118 +33,53 @@ export default class ExportDialog extends Component {
|
||||
});
|
||||
};
|
||||
|
||||
_renderExporterListItem = (exporter, index) => {
|
||||
return (
|
||||
<ListItem
|
||||
key={exporter.key}
|
||||
disabled={exporter.disabled}
|
||||
style={exporter.disabled ? styles.disabledItem : undefined}
|
||||
leftAvatar={exporter.renderIcon({ style: styles.icon })}
|
||||
primaryText={exporter.name}
|
||||
secondaryText={<p>{exporter.description}</p>}
|
||||
secondaryTextLines={2}
|
||||
onClick={() => this.chooseExporter(exporter.key)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
project,
|
||||
open,
|
||||
onClose,
|
||||
authentification,
|
||||
onChangeSubscription,
|
||||
exporters,
|
||||
} = this.props;
|
||||
const { showExperimental, chosenExporterKey } = this.state;
|
||||
const { project, open, onClose } = this.props;
|
||||
const { showExperimental } = this.state;
|
||||
if (!open || !project) return null;
|
||||
|
||||
const exporter = exporters.find(
|
||||
exporter => exporter.key === chosenExporterKey
|
||||
);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
title="Export project to a standalone game"
|
||||
onRequestClose={onClose}
|
||||
actions={[
|
||||
chosenExporterKey && (
|
||||
actions={
|
||||
<FlatButton label="Close" primary={false} onTouchTap={onClose} />
|
||||
}
|
||||
secondaryActions={[
|
||||
<HelpButton key="help" helpPagePath="/publishing" />,
|
||||
!showExperimental ? (
|
||||
<FlatButton
|
||||
label="Back"
|
||||
key="back"
|
||||
key="toggle-experimental"
|
||||
icon={<Visibility />}
|
||||
primary={false}
|
||||
onClick={() => this.chooseExporter('')}
|
||||
onTouchTap={() => this._showExperimental(true)}
|
||||
label="Show experimental exports"
|
||||
/>
|
||||
) : (
|
||||
<FlatButton
|
||||
key="toggle-experimental"
|
||||
icon={<VisibilityOff />}
|
||||
primary={false}
|
||||
onTouchTap={() => this._showExperimental(false)}
|
||||
label="Hide experimental exports"
|
||||
/>
|
||||
),
|
||||
<FlatButton
|
||||
label="Close"
|
||||
key="close"
|
||||
primary={false}
|
||||
onClick={onClose}
|
||||
/>,
|
||||
]}
|
||||
secondaryActions={[
|
||||
<HelpButton
|
||||
key="help"
|
||||
helpPagePath={(exporter && exporter.helpPage) || '/publishing'}
|
||||
/>,
|
||||
!chosenExporterKey &&
|
||||
(!showExperimental ? (
|
||||
<FlatButton
|
||||
key="toggle-experimental"
|
||||
icon={<Visibility />}
|
||||
primary={false}
|
||||
onClick={() => this._showExperimental(true)}
|
||||
label="Show experimental exports"
|
||||
/>
|
||||
) : (
|
||||
<FlatButton
|
||||
key="toggle-experimental"
|
||||
icon={<VisibilityOff />}
|
||||
primary={false}
|
||||
onClick={() => this._showExperimental(false)}
|
||||
label="Hide experimental exports"
|
||||
/>
|
||||
)),
|
||||
]}
|
||||
open={open}
|
||||
noMargin
|
||||
autoScrollBodyContent
|
||||
>
|
||||
{!exporter && (
|
||||
<List>
|
||||
{exporters
|
||||
.filter(exporter => !exporter.advanced && !exporter.experimental)
|
||||
.map((exporter, index) =>
|
||||
this._renderExporterListItem(exporter, index)
|
||||
)}
|
||||
|
||||
<Subheader>Advanced</Subheader>
|
||||
{exporters
|
||||
.filter(exporter => exporter.advanced)
|
||||
.map((exporter, index) =>
|
||||
this._renderExporterListItem(exporter, index)
|
||||
)}
|
||||
|
||||
{showExperimental && <Subheader>Experimental</Subheader>}
|
||||
{showExperimental &&
|
||||
exporters
|
||||
.filter(exporter => exporter.experimental)
|
||||
.map((exporter, index) =>
|
||||
this._renderExporterListItem(exporter, index)
|
||||
)}
|
||||
</List>
|
||||
)}
|
||||
{exporter && (
|
||||
<div style={styles.content}>
|
||||
<exporter.ExportComponent
|
||||
project={project}
|
||||
authentification={authentification}
|
||||
onChangeSubscription={onChangeSubscription}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<Tabs value={this.state.value} onChange={this._onChangeTab}>
|
||||
{this.props.tabs.map(
|
||||
({ ExportComponent, name, advanced }, index) =>
|
||||
(!advanced || showExperimental) && (
|
||||
<Tab label={name} value={index} key={index}>
|
||||
<div style={styles.tabContent}>
|
||||
<ExportComponent project={this.props.project} />
|
||||
</div>
|
||||
</Tab>
|
||||
)
|
||||
)}
|
||||
</Tabs>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
@@ -1,17 +1,17 @@
|
||||
import React, { Component } from 'react';
|
||||
import Dialog from '../../UI/Dialog';
|
||||
import Dialog from '../UI/Dialog';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
import Toggle from 'material-ui/Toggle';
|
||||
import { sendExportLaunched } from '../../Utils/Analytics/EventSender';
|
||||
import { Column, Line, Spacer } from '../../UI/Grid';
|
||||
import HelpButton from '../../UI/HelpButton';
|
||||
import { showErrorBox } from '../../UI/Messages/MessageBox';
|
||||
import { sendExportLaunched } from '../Utils/Analytics/EventSender';
|
||||
import { Column, Line, Spacer } from '../UI/Grid';
|
||||
import HelpButton from '../UI/HelpButton';
|
||||
import { showErrorBox } from '../UI/Messages/MessageBox';
|
||||
import { findGDJS } from './LocalGDJSFinder';
|
||||
import localFileSystem from './LocalFileSystem';
|
||||
import LocalFolderPicker from '../../UI/LocalFolderPicker';
|
||||
import LocalFolderPicker from '../UI/LocalFolderPicker';
|
||||
import assignIn from 'lodash/assignIn';
|
||||
import optionalRequire from '../../Utils/OptionalRequire';
|
||||
import optionalRequire from '../Utils/OptionalRequire';
|
||||
const electron = optionalRequire('electron');
|
||||
const shell = electron ? electron.shell : null;
|
||||
|
||||
@@ -114,7 +114,7 @@ export default class LocalCocos2dExport extends Component {
|
||||
<RaisedButton
|
||||
label="Export"
|
||||
primary={true}
|
||||
onClick={this.launchExport}
|
||||
onTouchTap={this.launchExport}
|
||||
disabled={!this.state.outputDir}
|
||||
/>
|
||||
</Line>
|
||||
@@ -125,13 +125,13 @@ export default class LocalCocos2dExport extends Component {
|
||||
key="open"
|
||||
label="Open folder"
|
||||
primary={true}
|
||||
onClick={this.openExportFolder}
|
||||
onTouchTap={this.openExportFolder}
|
||||
/>,
|
||||
<FlatButton
|
||||
key="close"
|
||||
label="Close"
|
||||
primary={false}
|
||||
onClick={() =>
|
||||
onTouchTap={() =>
|
||||
this.setState({
|
||||
exportFinishedDialogOpen: false,
|
||||
})}
|
@@ -1,17 +1,17 @@
|
||||
import React, { Component } from 'react';
|
||||
import Dialog from '../../UI/Dialog';
|
||||
import Dialog from '../UI/Dialog';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
import { sendExportLaunched } from '../../Utils/Analytics/EventSender';
|
||||
import { Column, Line, Spacer } from '../../UI/Grid';
|
||||
import { showErrorBox } from '../../UI/Messages/MessageBox';
|
||||
import { sendExportLaunched } from '../Utils/Analytics/EventSender';
|
||||
import { Column, Line, Spacer } from '../UI/Grid';
|
||||
import { showErrorBox } from '../UI/Messages/MessageBox';
|
||||
import { findGDJS } from './LocalGDJSFinder';
|
||||
import localFileSystem from './LocalFileSystem';
|
||||
import LocalFolderPicker from '../../UI/LocalFolderPicker';
|
||||
import HelpButton from '../../UI/HelpButton';
|
||||
import LocalFolderPicker from '../UI/LocalFolderPicker';
|
||||
import HelpButton from '../UI/HelpButton';
|
||||
import assignIn from 'lodash/assignIn';
|
||||
import optionalRequire from '../../Utils/OptionalRequire';
|
||||
import Window from '../../Utils/Window';
|
||||
import optionalRequire from '../Utils/OptionalRequire';
|
||||
import Window from '../Utils/Window';
|
||||
const electron = optionalRequire('electron');
|
||||
const shell = electron ? electron.shell : null;
|
||||
|
||||
@@ -120,7 +120,7 @@ export default class LocalCordovaExport extends Component {
|
||||
<RaisedButton
|
||||
label="Export"
|
||||
primary={true}
|
||||
onClick={this.launchExport}
|
||||
onTouchTap={this.launchExport}
|
||||
disabled={!this.state.outputDir}
|
||||
/>
|
||||
</Line>
|
||||
@@ -131,13 +131,13 @@ export default class LocalCordovaExport extends Component {
|
||||
key="open"
|
||||
label="Open folder"
|
||||
primary={true}
|
||||
onClick={this.openExportFolder}
|
||||
onTouchTap={this.openExportFolder}
|
||||
/>,
|
||||
<FlatButton
|
||||
key="close"
|
||||
label="Close"
|
||||
primary={false}
|
||||
onClick={() =>
|
||||
onTouchTap={() =>
|
||||
this.setState({
|
||||
exportFinishedDialogOpen: false,
|
||||
})}
|
||||
@@ -161,7 +161,7 @@ export default class LocalCordovaExport extends Component {
|
||||
<RaisedButton
|
||||
fullWidth
|
||||
primary
|
||||
onClick={() => this.openPhoneGapBuild()}
|
||||
onTouchTap={() => this.openPhoneGapBuild()}
|
||||
label="Open PhoneGap Build"
|
||||
/>
|
||||
</Dialog>
|
@@ -1,17 +1,17 @@
|
||||
import React, { Component } from 'react';
|
||||
import Dialog from '../../UI/Dialog';
|
||||
import Dialog from '../UI/Dialog';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
import { sendExportLaunched } from '../../Utils/Analytics/EventSender';
|
||||
import { Column, Line, Spacer } from '../../UI/Grid';
|
||||
import { showErrorBox } from '../../UI/Messages/MessageBox';
|
||||
import { sendExportLaunched } from '../Utils/Analytics/EventSender';
|
||||
import { Column, Line, Spacer } from '../UI/Grid';
|
||||
import { showErrorBox } from '../UI/Messages/MessageBox';
|
||||
import { findGDJS } from './LocalGDJSFinder';
|
||||
import localFileSystem from './LocalFileSystem';
|
||||
import LocalFolderPicker from '../../UI/LocalFolderPicker';
|
||||
import LocalFolderPicker from '../UI/LocalFolderPicker';
|
||||
import assignIn from 'lodash/assignIn';
|
||||
import optionalRequire from '../../Utils/OptionalRequire';
|
||||
import Window from '../../Utils/Window';
|
||||
import { getHelpLink } from '../../Utils/HelpLink';
|
||||
import optionalRequire from '../Utils/OptionalRequire';
|
||||
import Window from '../Utils/Window';
|
||||
import { getHelpLink } from '../Utils/HelpLink';
|
||||
const electron = optionalRequire('electron');
|
||||
const shell = electron ? electron.shell : null;
|
||||
|
||||
@@ -115,7 +115,7 @@ export default class LocalExport extends Component {
|
||||
<RaisedButton
|
||||
label="Export"
|
||||
primary={true}
|
||||
onClick={this.launchExport}
|
||||
onTouchTap={this.launchExport}
|
||||
disabled={!this.state.outputDir}
|
||||
/>
|
||||
</Line>
|
||||
@@ -126,13 +126,13 @@ export default class LocalExport extends Component {
|
||||
key="open"
|
||||
label="Open folder"
|
||||
primary={true}
|
||||
onClick={this.openExportFolder}
|
||||
onTouchTap={this.openExportFolder}
|
||||
/>,
|
||||
<FlatButton
|
||||
key="close"
|
||||
label="Close"
|
||||
primary={false}
|
||||
onClick={() =>
|
||||
onTouchTap={() =>
|
||||
this.setState({
|
||||
exportFinishedDialogOpen: false,
|
||||
})}
|
||||
@@ -147,12 +147,12 @@ export default class LocalExport extends Component {
|
||||
<RaisedButton
|
||||
fullWidth
|
||||
primary
|
||||
onClick={() => this.openItchioHelp()}
|
||||
onTouchTap={() => this.openItchioHelp()}
|
||||
label="Publish your game on Itch.io"
|
||||
/>
|
||||
<FlatButton
|
||||
fullWidth
|
||||
onClick={() => this.openLearnMore()}
|
||||
onTouchTap={() => this.openLearnMore()}
|
||||
label="Learn more about publishing"
|
||||
/>
|
||||
</Dialog>
|
@@ -1,35 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import optionalRequire from '../../../Utils/OptionalRequire.js';
|
||||
const archiver = optionalRequire('archiver');
|
||||
const fs = optionalRequire('fs');
|
||||
|
||||
export const archiveFolder = ({
|
||||
path,
|
||||
outputFilename,
|
||||
}: {
|
||||
path: string,
|
||||
outputFilename: string,
|
||||
}): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const output = fs.createWriteStream(outputFilename);
|
||||
const archive = archiver('zip', {
|
||||
zlib: { level: 9 }, // Sets the compression level.
|
||||
});
|
||||
|
||||
output.on('close', () => {
|
||||
console.log(`Archive written at ${outputFilename}, ${archive.pointer()} total bytes.`);
|
||||
resolve(outputFilename);
|
||||
});
|
||||
|
||||
archive.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
|
||||
archive.pipe(output);
|
||||
|
||||
archive.directory(path, false);
|
||||
|
||||
archive.finalize();
|
||||
});
|
||||
};
|
@@ -1,106 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
import { Step, Stepper, StepLabel, StepContent } from 'material-ui/Stepper';
|
||||
import CircularProgress from 'material-ui/CircularProgress';
|
||||
import LinearProgress from 'material-ui/LinearProgress';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import { Line, Spacer } from '../../../UI/Grid';
|
||||
|
||||
export default ({
|
||||
exportStep,
|
||||
downloadUrl,
|
||||
onDownload,
|
||||
onDownloadLogs,
|
||||
uploadMax,
|
||||
uploadProgress,
|
||||
buildMax,
|
||||
buildProgress,
|
||||
errored,
|
||||
}) => (
|
||||
<Stepper
|
||||
activeStep={
|
||||
exportStep === 'export'
|
||||
? 0
|
||||
: exportStep === 'compress' || exportStep === 'upload'
|
||||
? 1
|
||||
: exportStep === 'waiting-for-build' || exportStep === 'build'
|
||||
? 2
|
||||
: exportStep === 'done' ? 3 : undefined
|
||||
}
|
||||
orientation="vertical"
|
||||
>
|
||||
<Step>
|
||||
<StepLabel>Game export</StepLabel>
|
||||
<StepContent>
|
||||
<Line alignItems="center">
|
||||
<CircularProgress size={20} />
|
||||
<Spacer />
|
||||
<p>Export in progress...</p>
|
||||
</Line>
|
||||
</StepContent>
|
||||
</Step>
|
||||
<Step>
|
||||
<StepLabel>Upload to build service</StepLabel>
|
||||
<StepContent>
|
||||
{errored ? (
|
||||
<p>
|
||||
Can't upload your game to the build service. Please check your
|
||||
internet connection or try again later
|
||||
</p>
|
||||
) : exportStep === 'compress' ? (
|
||||
<Line alignItems="center">
|
||||
<CircularProgress size={20} />
|
||||
<Spacer />
|
||||
<p>Compressing before upload...</p>
|
||||
</Line>
|
||||
) : (
|
||||
<Line alignItems="center" expand>
|
||||
<LinearProgress
|
||||
style={{ flex: 1 }}
|
||||
max={uploadMax}
|
||||
value={uploadProgress}
|
||||
mode="determinate"
|
||||
/>
|
||||
</Line>
|
||||
)}
|
||||
</StepContent>
|
||||
</Step>
|
||||
<Step>
|
||||
<StepLabel>Build</StepLabel>
|
||||
<StepContent>
|
||||
{errored ? (
|
||||
<p>
|
||||
Something wrong happened :(
|
||||
</p>
|
||||
) : exportStep === 'waiting-for-build' ? (
|
||||
<Line alignItems="center">
|
||||
<CircularProgress size={20} />
|
||||
<Spacer />
|
||||
<p>Waiting for build to start...</p>
|
||||
</Line>
|
||||
) : (
|
||||
<Line alignItems="center" expand>
|
||||
<LinearProgress
|
||||
style={{ flex: 1 }}
|
||||
max={buildMax}
|
||||
value={buildProgress}
|
||||
mode="determinate"
|
||||
/>
|
||||
</Line>
|
||||
)}
|
||||
</StepContent>
|
||||
</Step>
|
||||
<Step>
|
||||
<StepLabel>Download</StepLabel>
|
||||
<StepContent>
|
||||
<Line>
|
||||
<RaisedButton label="Download" primary onClick={onDownload} />
|
||||
<FlatButton label="See logs" onClick={onDownloadLogs} />
|
||||
</Line>
|
||||
<Line expand>
|
||||
You can download it on your Android phone and install it.
|
||||
</Line>
|
||||
</StepContent>
|
||||
</Step>
|
||||
</Stepper>
|
||||
);
|
@@ -1,352 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import assignIn from 'lodash/assignIn';
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
import { sendExportLaunched } from '../../../Utils/Analytics/EventSender';
|
||||
import {
|
||||
type Build,
|
||||
buildCordovaAndroid,
|
||||
getUrl,
|
||||
getBuild,
|
||||
} from '../../../Utils/GDevelopServices/Build';
|
||||
import {
|
||||
withUserProfile,
|
||||
type WithUserProfileProps,
|
||||
} from '../../../Profile/UserProfileContainer';
|
||||
import { Column, Line } from '../../../UI/Grid';
|
||||
import { showErrorBox } from '../../../UI/Messages/MessageBox';
|
||||
import { findGDJS } from '../LocalGDJSFinder';
|
||||
import localFileSystem from '../LocalFileSystem';
|
||||
import Progress from './Progress';
|
||||
import { archiveFolder } from './Archiver';
|
||||
import optionalRequire from '../../../Utils/OptionalRequire.js';
|
||||
import Window from '../../../Utils/Window';
|
||||
import { delay } from '../../../Utils/Delay';
|
||||
import CreateProfile from '../../../Profile/CreateProfile';
|
||||
import LimitDisplayer from '../../../Profile/LimitDisplayer';
|
||||
const path = optionalRequire('path');
|
||||
const os = optionalRequire('os');
|
||||
const electron = optionalRequire('electron');
|
||||
const ipcRenderer = electron ? electron.ipcRenderer : null;
|
||||
|
||||
const gd = global.gd;
|
||||
|
||||
export type LocalOnlineCordovaExportStep =
|
||||
| ''
|
||||
| 'export'
|
||||
| 'compress'
|
||||
| 'upload'
|
||||
| 'waiting-for-build'
|
||||
| 'build'
|
||||
| 'done';
|
||||
|
||||
type State = {
|
||||
exportStep: LocalOnlineCordovaExportStep,
|
||||
build: ?Build,
|
||||
uploadProgress: number,
|
||||
uploadMax: number,
|
||||
buildMax: number,
|
||||
buildProgress: number,
|
||||
errored: boolean,
|
||||
};
|
||||
|
||||
type Props = WithUserProfileProps & {
|
||||
project: gdProject,
|
||||
onChangeSubscription: Function,
|
||||
};
|
||||
|
||||
class LocalOnlineCordovaExport extends Component<Props, State> {
|
||||
state = {
|
||||
exportStep: '',
|
||||
build: null,
|
||||
uploadProgress: 0,
|
||||
uploadMax: 0,
|
||||
buildProgress: 0,
|
||||
buildMax: 0,
|
||||
errored: false,
|
||||
};
|
||||
|
||||
static prepareExporter = (): Promise<any> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
findGDJS(gdjsRoot => {
|
||||
if (!gdjsRoot) {
|
||||
showErrorBox('Could not find GDJS');
|
||||
return reject();
|
||||
}
|
||||
console.info('GDJS found in ', gdjsRoot);
|
||||
|
||||
const fileSystem = assignIn(
|
||||
new gd.AbstractFileSystemJS(),
|
||||
localFileSystem
|
||||
);
|
||||
const exporter = new gd.Exporter(fileSystem, gdjsRoot);
|
||||
const outputDir = path.join(
|
||||
fileSystem.getTempDir(),
|
||||
'OnlineCordovaExport'
|
||||
);
|
||||
fileSystem.mkDir(outputDir);
|
||||
fileSystem.clearDir(outputDir);
|
||||
|
||||
resolve({
|
||||
exporter,
|
||||
outputDir,
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
launchExport = (): Promise<string> => {
|
||||
const { project } = this.props;
|
||||
if (!project) return Promise.reject();
|
||||
|
||||
return LocalOnlineCordovaExport.prepareExporter()
|
||||
.then(({ exporter, outputDir }) => {
|
||||
const exportForCordova = true;
|
||||
exporter.exportWholePixiProject(
|
||||
project,
|
||||
outputDir,
|
||||
false,
|
||||
exportForCordova
|
||||
);
|
||||
exporter.delete();
|
||||
|
||||
return outputDir;
|
||||
})
|
||||
.catch(err => {
|
||||
showErrorBox('Unable to export the game', err);
|
||||
throw err;
|
||||
});
|
||||
};
|
||||
|
||||
launchCompression = (outputDir: string): Promise<string> => {
|
||||
const archiveOutputDir = os.tmpdir();
|
||||
return archiveFolder({
|
||||
path: outputDir,
|
||||
outputFilename: path.join(archiveOutputDir, 'game-archive.zip'),
|
||||
});
|
||||
};
|
||||
|
||||
launchUpload = (outputFile: string): Promise<string> => {
|
||||
if (!ipcRenderer) return Promise.reject('No support for upload');
|
||||
|
||||
ipcRenderer.removeAllListeners('s3-file-upload-progress');
|
||||
ipcRenderer.removeAllListeners('s3-file-upload-done');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
ipcRenderer.on(
|
||||
's3-file-upload-progress',
|
||||
(event, uploadProgress, uploadMax) => {
|
||||
this.setState({
|
||||
uploadProgress,
|
||||
uploadMax,
|
||||
});
|
||||
}
|
||||
);
|
||||
ipcRenderer.on('s3-file-upload-done', (event, err, prefix) => {
|
||||
if (err) return reject(err);
|
||||
resolve(prefix);
|
||||
});
|
||||
ipcRenderer.send('s3-file-upload', outputFile);
|
||||
});
|
||||
};
|
||||
|
||||
launchBuild = (uploadBucketKey: string): Promise<string> => {
|
||||
const { authentification, profile } = this.props;
|
||||
if (!profile || !authentification)
|
||||
return Promise.reject(new Error('User is not authenticated'));
|
||||
|
||||
return buildCordovaAndroid(
|
||||
authentification,
|
||||
profile.uid,
|
||||
uploadBucketKey
|
||||
).then(build => {
|
||||
return build.id;
|
||||
});
|
||||
};
|
||||
|
||||
pollBuild = async (buildId: string): Promise<Build> => {
|
||||
const { authentification, profile } = this.props;
|
||||
if (!profile || !authentification)
|
||||
return Promise.reject(new Error('User is not authenticated'));
|
||||
|
||||
try {
|
||||
let build = null;
|
||||
let tries = 0;
|
||||
const waitTime = 1000;
|
||||
const maxWaitTime = 200000;
|
||||
do {
|
||||
await delay(waitTime);
|
||||
build = await getBuild(authentification, profile.uid, buildId);
|
||||
this.setState({
|
||||
build,
|
||||
buildMax: maxWaitTime,
|
||||
buildProgress: tries * waitTime,
|
||||
});
|
||||
tries += 1;
|
||||
} while (
|
||||
build &&
|
||||
build.status === 'pending' &&
|
||||
tries * waitTime < maxWaitTime
|
||||
);
|
||||
|
||||
if (build.status !== 'complete') throw build;
|
||||
return build;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
launchWholeExport = () => {
|
||||
sendExportLaunched('local-online-cordova');
|
||||
|
||||
const handleError = (message: string) => err => {
|
||||
if (!this.state.errored) {
|
||||
this.setState({
|
||||
errored: true,
|
||||
});
|
||||
showErrorBox(message, {
|
||||
exportStep: this.state.exportStep,
|
||||
rawError: err,
|
||||
});
|
||||
}
|
||||
|
||||
throw err;
|
||||
};
|
||||
|
||||
this.setState({
|
||||
exportStep: 'export',
|
||||
uploadProgress: 0,
|
||||
uploadMax: 0,
|
||||
errored: false,
|
||||
});
|
||||
this.launchExport()
|
||||
.then(outputDir => {
|
||||
this.setState({
|
||||
exportStep: 'compress',
|
||||
});
|
||||
return this.launchCompression(outputDir);
|
||||
}, handleError('Error while exporting the game.'))
|
||||
.then(outputFile => {
|
||||
this.setState({
|
||||
exportStep: 'upload',
|
||||
});
|
||||
return this.launchUpload(outputFile);
|
||||
}, handleError('Error while compressing the game.'))
|
||||
.then((uploadBucketKey: string) => {
|
||||
this.setState({
|
||||
exportStep: 'waiting-for-build',
|
||||
});
|
||||
return this.launchBuild(uploadBucketKey);
|
||||
}, handleError('Error while uploading the game. Check your internet connection or try again later.'))
|
||||
.then(buildId => {
|
||||
this.setState({
|
||||
exportStep: 'build',
|
||||
});
|
||||
|
||||
return this.pollBuild(buildId);
|
||||
}, handleError('Error while lauching the build of the game.'))
|
||||
.then(build => {
|
||||
this.setState({
|
||||
exportStep: 'done',
|
||||
build,
|
||||
});
|
||||
this.props.onRefreshUserProfile();
|
||||
}, handleError('Error while building the game.'));
|
||||
};
|
||||
|
||||
_download = () => {
|
||||
const { build } = this.state;
|
||||
if (!build || !build.apkKey) return;
|
||||
|
||||
Window.openExternalURL(getUrl(build.apkKey));
|
||||
};
|
||||
|
||||
_downloadLogs = () => {
|
||||
const { build } = this.state;
|
||||
if (!build || !build.logsKey) return;
|
||||
|
||||
Window.openExternalURL(getUrl(build.logsKey));
|
||||
};
|
||||
|
||||
_onChangeSubscription = () => {
|
||||
const { onChangeSubscription } = this.props;
|
||||
|
||||
onChangeSubscription();
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
exportStep,
|
||||
build,
|
||||
uploadMax,
|
||||
uploadProgress,
|
||||
buildMax,
|
||||
buildProgress,
|
||||
errored,
|
||||
} = this.state;
|
||||
const {
|
||||
project,
|
||||
authenticated,
|
||||
onLogin,
|
||||
subscription,
|
||||
limits,
|
||||
} = this.props;
|
||||
if (!project) return null;
|
||||
|
||||
const buildLimit = limits ? limits['cordova-build'] : null;
|
||||
const disableBuild =
|
||||
(!errored && exportStep !== '' && exportStep !== 'done') ||
|
||||
(buildLimit && buildLimit.limitReached);
|
||||
|
||||
return (
|
||||
<Column noMargin>
|
||||
<Line>
|
||||
Packaging your game for Android will create an APK file that can be
|
||||
installed on Android phones, based on Cordova framework.
|
||||
</Line>
|
||||
{authenticated && (
|
||||
<Line justifyContent="center">
|
||||
<RaisedButton
|
||||
label="Package for Android"
|
||||
primary
|
||||
onClick={this.launchWholeExport}
|
||||
disabled={disableBuild}
|
||||
/>
|
||||
</Line>
|
||||
)}
|
||||
{authenticated && (
|
||||
<LimitDisplayer
|
||||
subscription={subscription}
|
||||
limit={buildLimit}
|
||||
onChangeSubscription={this._onChangeSubscription}
|
||||
/>
|
||||
)}
|
||||
{!authenticated && (
|
||||
<CreateProfile
|
||||
message="Create an account to build your game for Android in one-click:"
|
||||
onLogin={onLogin}
|
||||
/>
|
||||
)}
|
||||
<Line>
|
||||
<Progress
|
||||
exportStep={exportStep}
|
||||
downloadUrl={build && build.apkKey ? getUrl(build.apkKey) : null}
|
||||
logsUrl={build && build.logsKey ? getUrl(build.logsKey) : null}
|
||||
onDownload={this._download}
|
||||
onDownloadLogs={this._downloadLogs}
|
||||
uploadMax={uploadMax}
|
||||
uploadProgress={uploadProgress}
|
||||
buildMax={buildMax}
|
||||
buildProgress={buildProgress}
|
||||
errored={errored}
|
||||
/>
|
||||
</Line>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withUserProfile({ fetchLimits: true, fetchSubscription: true })(
|
||||
LocalOnlineCordovaExport
|
||||
);
|
@@ -1,28 +0,0 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import Window from '../../Utils/Window';
|
||||
import PlaceholderMessage from '../../UI/PlaceholderMessage';
|
||||
|
||||
export default class LocalOnlineElectronExport extends React.Component<*, *> {
|
||||
_openGithub() {
|
||||
Window.openExternalURL('https://github.com/4ian/GD');
|
||||
}
|
||||
|
||||
render() {
|
||||
const { project } = this.props;
|
||||
if (!project) return null;
|
||||
|
||||
return (
|
||||
<div style={{ height: 300 }}>
|
||||
<PlaceholderMessage>
|
||||
<p>This export is not ready yet!</p>
|
||||
<FlatButton
|
||||
label="Help by contributing on GitHub"
|
||||
onClick={this._openGithub}
|
||||
/>
|
||||
</PlaceholderMessage>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,68 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import LocalExport from './LocalExport';
|
||||
import LocalS3Export from './LocalS3Export';
|
||||
import LocalOnlineCordovaExport from './LocalOnlineCordovaExport';
|
||||
import LocalCordovaExport from './LocalCordovaExport';
|
||||
import LocalCocos2dExport from './LocalCocos2dExport';
|
||||
import LocalOnlineElectronExport from './LocalOnlineElectronExport';
|
||||
import PhoneIphone from 'material-ui/svg-icons/hardware/phone-iphone';
|
||||
import LaptopMac from 'material-ui/svg-icons/hardware/laptop-mac';
|
||||
import Folder from 'material-ui/svg-icons/file/folder';
|
||||
import Devices from 'material-ui/svg-icons/device/devices';
|
||||
|
||||
export const getLocalExporters = () => [
|
||||
{
|
||||
name: 'Android (& iOS coming soon)',
|
||||
renderIcon: (props) => <PhoneIphone {...props}/>,
|
||||
helpPage: '/publishing/android_and_ios',
|
||||
description: 'Package your game for Android directly from GDevelop. iOS support is coming soon!',
|
||||
key: 'localonlinecordovaexport',
|
||||
ExportComponent: LocalOnlineCordovaExport,
|
||||
},
|
||||
{
|
||||
name: 'Web (upload online)',
|
||||
renderIcon: (props) => <Devices {...props}/>,
|
||||
helpPage: '/publishing/web',
|
||||
description: 'Upload your game online directly from GDevelop and share the link to players. Play to your game using your browser on computers and mobile phones.',
|
||||
key: 'locals3export',
|
||||
ExportComponent: LocalS3Export,
|
||||
},
|
||||
{
|
||||
name: 'Local folder',
|
||||
renderIcon: (props) => <Folder {...props}/>,
|
||||
helpPage: '/publishing/html5_game_in_a_local_folder',
|
||||
description:
|
||||
'Build the game locally as a HTML5 game. You can then export it on website like Itch.io or Kongregate.',
|
||||
key: 'localexport',
|
||||
ExportComponent: LocalExport,
|
||||
advanced: true,
|
||||
},
|
||||
{
|
||||
name: 'iOS & Android (manual)',
|
||||
renderIcon: (props) => <PhoneIphone {...props}/>,
|
||||
helpPage: '/publishing/android_and_ios_with_cordova',
|
||||
description:
|
||||
'Build the game locally as a Cordova project, and export it manually then to iOS or Android with Cordova developers tools.',
|
||||
key: 'localcordovaexport',
|
||||
ExportComponent: LocalCordovaExport,
|
||||
advanced: true,
|
||||
},
|
||||
{
|
||||
name: 'Windows/macOS/Linux (coming soon)',
|
||||
renderIcon: (props) => <LaptopMac {...props}/>,
|
||||
description: 'Package your game as an app for Windows, macOs or Linux.',
|
||||
disabled: true,
|
||||
key: 'localonlineelectronexport',
|
||||
ExportComponent: LocalOnlineElectronExport,
|
||||
},
|
||||
{
|
||||
name: 'Cocos2d-JS',
|
||||
renderIcon: (props) => <PhoneIphone {...props}/>,
|
||||
helpPage: '/publishing/android_and_ios_with_cocos2d-js',
|
||||
description:
|
||||
'Export your game using Cocos2d-JS game engine. The game can be compiled for Android or iOS if you install Cocos2d-JS developer tools.',
|
||||
key: 'localcocos2dexport',
|
||||
ExportComponent: LocalCocos2dExport,
|
||||
experimental: true,
|
||||
},
|
||||
];
|
@@ -1,4 +1,4 @@
|
||||
import optionalRequire from '../../Utils/OptionalRequire.js';
|
||||
import optionalRequire from '../Utils/OptionalRequire.js';
|
||||
var fs = optionalRequire('fs-extra');
|
||||
var path = optionalRequire('path');
|
||||
var os = optionalRequire('os');
|
@@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import optionalRequire from '../../Utils/OptionalRequire.js';
|
||||
import optionalRequire from '../Utils/OptionalRequire.js';
|
||||
const electron = optionalRequire('electron');
|
||||
const app = electron ? electron.remote.app : null;
|
||||
const fs = optionalRequire('fs');
|
@@ -1,8 +1,8 @@
|
||||
// @flow
|
||||
|
||||
import localFileSystem from './LocalFileSystem';
|
||||
import optionalRequire from '../../Utils/OptionalRequire';
|
||||
import { timeFunction } from '../../Utils/TimeFunction';
|
||||
import optionalRequire from '../Utils/OptionalRequire';
|
||||
import { timeFunction } from '../Utils/TimeFunction';
|
||||
import { findGDJS } from './LocalGDJSFinder';
|
||||
import assignIn from 'lodash/assignIn';
|
||||
const electron = optionalRequire('electron');
|
||||
@@ -11,7 +11,7 @@ const BrowserWindow = electron ? electron.remote.BrowserWindow : null;
|
||||
const gd = global.gd;
|
||||
|
||||
export default class LocalPreviewLauncher {
|
||||
static _openPreviewWindow = (project: gdProject, gamePath: string): void => {
|
||||
static _openPreviewWindow = (project, gamePath): void => {
|
||||
if (!BrowserWindow) return;
|
||||
|
||||
const win = new BrowserWindow({
|
||||
@@ -47,10 +47,7 @@ export default class LocalPreviewLauncher {
|
||||
});
|
||||
};
|
||||
|
||||
static launchLayoutPreview = (
|
||||
project: gdProject,
|
||||
layout: gdLayout
|
||||
): Promise<any> => {
|
||||
static launchLayoutPreview = (project, layout): Promise<any> => {
|
||||
if (!project || !layout) return Promise.reject();
|
||||
|
||||
return LocalPreviewLauncher._prepareExporter().then(
|
||||
@@ -68,9 +65,9 @@ export default class LocalPreviewLauncher {
|
||||
};
|
||||
|
||||
static launchExternalLayoutPreview = (
|
||||
project: gdProject,
|
||||
layout: gdLayout,
|
||||
externalLayout: gdExternalLayout
|
||||
project,
|
||||
layout,
|
||||
externalLayout
|
||||
): Promise<any> => {
|
||||
if (!project || !externalLayout) return Promise.reject();
|
||||
|
@@ -2,18 +2,21 @@ import React, { Component } from 'react';
|
||||
import axios from 'axios';
|
||||
import { sleep } from 'wait-promise';
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
import { sendExportLaunched } from '../../Utils/Analytics/EventSender';
|
||||
import { sendExportLaunched } from '../Utils/Analytics/EventSender';
|
||||
import LocalExport from './LocalExport';
|
||||
import optionalRequire from '../../Utils/OptionalRequire';
|
||||
import { Column, Line, Spacer } from '../../UI/Grid';
|
||||
import optionalRequire from '../Utils/OptionalRequire';
|
||||
import { Column, Line, Spacer } from '../UI/Grid';
|
||||
import LinearProgress from 'material-ui/LinearProgress';
|
||||
import { GDevelopHostingApi } from '../../Utils/GDevelopServices/ApiConfigs';
|
||||
import TextField from 'material-ui/TextField';
|
||||
const os = optionalRequire('os');
|
||||
const electron = optionalRequire('electron');
|
||||
const ipcRenderer = electron ? electron.ipcRenderer : null;
|
||||
const shell = electron ? electron.shell : null;
|
||||
|
||||
const deployEndpoint =
|
||||
'https://nik50aqlp6.execute-api.eu-west-1.amazonaws.com/Production/deploy';
|
||||
const gamesHost = 'http://gd-games.s3-website-eu-west-1.amazonaws.com';
|
||||
|
||||
export default class LocalS3Export extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -27,17 +30,17 @@ export default class LocalS3Export extends Component {
|
||||
}
|
||||
|
||||
_uploadToS3 = localDir => {
|
||||
ipcRenderer.removeAllListeners('s3-folder-upload-progress');
|
||||
ipcRenderer.removeAllListeners('s3-folder-upload-done');
|
||||
ipcRenderer.removeAllListeners('s3-upload-progress');
|
||||
ipcRenderer.removeAllListeners('s3-upload-done');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
ipcRenderer.on('s3-folder-upload-progress', (event, uploadProgress, uploadMax) =>
|
||||
ipcRenderer.on('s3-upload-progress', (event, uploadProgress, uploadMax) =>
|
||||
this.setState({
|
||||
uploadProgress,
|
||||
uploadMax,
|
||||
})
|
||||
);
|
||||
ipcRenderer.on('s3-folder-upload-done', (event, err, prefix) => {
|
||||
ipcRenderer.on('s3-upload-done', (event, err, prefix) => {
|
||||
if (err) return reject(err);
|
||||
|
||||
this.setState({
|
||||
@@ -45,14 +48,14 @@ export default class LocalS3Export extends Component {
|
||||
});
|
||||
resolve(prefix);
|
||||
});
|
||||
ipcRenderer.send('s3-folder-upload', localDir);
|
||||
ipcRenderer.send('s3-upload', localDir);
|
||||
});
|
||||
};
|
||||
|
||||
_deploy = prefix => {
|
||||
return sleep(200)
|
||||
.then(() => //TODO: Move this to a GDevelopServices/Hosting.js file
|
||||
axios(GDevelopHostingApi.deployEndpoint, {
|
||||
.then(() =>
|
||||
axios(deployEndpoint, {
|
||||
method: 'post',
|
||||
params: {
|
||||
name: prefix,
|
||||
@@ -98,9 +101,8 @@ export default class LocalS3Export extends Component {
|
||||
.then(() => this._uploadToS3(outputDir))
|
||||
.then(uploadPrefix => this._deploy(uploadPrefix))
|
||||
.then(deployPrefix => {
|
||||
//TODO: Move this to a function getURL in a GDevelopServices/Hosting.js file.
|
||||
this.setState({
|
||||
url: `${GDevelopHostingApi.gamesHost}/${deployPrefix}`,
|
||||
url: `${gamesHost}/${deployPrefix}`,
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
@@ -125,7 +127,7 @@ export default class LocalS3Export extends Component {
|
||||
<Spacer />
|
||||
<TextField value={this.state.url} style={{ flex: 1 }} />
|
||||
<Spacer />
|
||||
<RaisedButton label="Open" primary={true} onClick={this.openURL} />
|
||||
<RaisedButton label="Open" primary={true} onTouchTap={this.openURL} />
|
||||
</Line>
|
||||
);
|
||||
};
|
||||
@@ -165,7 +167,7 @@ export default class LocalS3Export extends Component {
|
||||
<RaisedButton
|
||||
label="Export and upload my game"
|
||||
primary={true}
|
||||
onClick={this.launchExport}
|
||||
onTouchTap={this.launchExport}
|
||||
disabled={exportStarted}
|
||||
/>
|
||||
</Line>
|
@@ -172,7 +172,7 @@ class ExternalEditor extends Component<Props, State> {
|
||||
this.editorOpened = true;
|
||||
|
||||
if (this.props.editor === 'scene-editor') {
|
||||
this.editor.openLayout(this.props.editedElementName, {openEventsEditor: false});
|
||||
this.editor.openLayout(this.props.editedElementName, false);
|
||||
}
|
||||
if (this.props.editor === 'external-layout-editor') {
|
||||
this.editor.openExternalLayout(this.props.editedElementName);
|
||||
|
@@ -1,35 +0,0 @@
|
||||
// @flow
|
||||
|
||||
/**
|
||||
* Provide a method shouldUpdate that can be called in a game loop or in
|
||||
* a method used in renderAnimationFrame, and which indicate if the rendering/update
|
||||
* of the scene should be done according to the desired framerate.
|
||||
*/
|
||||
export default class FpsLimiter {
|
||||
_lastFrameTime: number;
|
||||
_interval: number;
|
||||
_forceUpdate: boolean;
|
||||
|
||||
constructor(maxFps: number) {
|
||||
this._lastFrameTime = Date.now();
|
||||
this._interval = 1000 / maxFps;
|
||||
this._forceUpdate = false;
|
||||
}
|
||||
|
||||
shouldUpdate() {
|
||||
const now = Date.now();
|
||||
const delta = now - this._lastFrameTime;
|
||||
|
||||
if (delta > this._interval || this._forceUpdate) {
|
||||
this._lastFrameTime = now - delta % this._interval;
|
||||
this._forceUpdate = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
forceNextUpdate() {
|
||||
this._forceUpdate = true;
|
||||
}
|
||||
}
|
@@ -99,13 +99,7 @@ export const addScrollbars = (WrappedComponent: any) => {
|
||||
);
|
||||
};
|
||||
|
||||
_setAndAdjust = ({
|
||||
xValue,
|
||||
yValue,
|
||||
}: {
|
||||
xValue: number,
|
||||
yValue: number,
|
||||
}) => {
|
||||
_setAndAdjust = ({ xValue, yValue }: {xValue: number, yValue: number}) => {
|
||||
const xMax = Math.max(Math.abs(xValue) + 100, this.state.xMax);
|
||||
const yMax = Math.max(Math.abs(yValue) + 100, this.state.yMax);
|
||||
|
||||
|
@@ -15,7 +15,6 @@ import WindowMask from './WindowMask';
|
||||
import DropHandler from './DropHandler';
|
||||
import BackgroundColor from './BackgroundColor';
|
||||
import PIXI from 'pixi.js';
|
||||
import FpsLimiter from './FpsLimiter';
|
||||
|
||||
export default class InstancesEditorContainer extends Component {
|
||||
constructor() {
|
||||
@@ -25,8 +24,6 @@ export default class InstancesEditorContainer extends Component {
|
||||
this.lastContextMenuY = 0;
|
||||
this.lastCursorX = 0;
|
||||
this.lastCursorY = 0;
|
||||
|
||||
this.fpsLimiter = new FpsLimiter(28);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -90,12 +87,7 @@ export default class InstancesEditorContainer extends Component {
|
||||
this._onMouseMove(event.data.global.x, event.data.global.y)
|
||||
);
|
||||
this.backgroundArea.on('panmove', event =>
|
||||
this._onPanMove(
|
||||
event.deltaX,
|
||||
event.deltaY,
|
||||
event.data.global.x,
|
||||
event.data.global.y
|
||||
)
|
||||
this._onPanMove(event.deltaX, event.deltaY, event.data.global.x, event.data.global.y)
|
||||
);
|
||||
this.backgroundArea.on('panend', event => this._onEndSelectionRectangle());
|
||||
this.pixiContainer.addChild(this.backgroundArea);
|
||||
@@ -254,10 +246,7 @@ export default class InstancesEditorContainer extends Component {
|
||||
nextProps.width,
|
||||
nextProps.height
|
||||
);
|
||||
|
||||
// Avoid flickering that could happen while waiting for next animation frame.
|
||||
this.fpsLimiter.forceNextUpdate();
|
||||
this._renderScene();
|
||||
this._renderScene(); //Avoid flickering that could happen while waiting for next animation frame.
|
||||
}
|
||||
|
||||
if (nextProps.options !== this.props.options) {
|
||||
@@ -471,25 +460,22 @@ export default class InstancesEditorContainer extends Component {
|
||||
|
||||
getViewPosition = () => {
|
||||
return this.viewPosition;
|
||||
};
|
||||
}
|
||||
|
||||
_renderScene = () => {
|
||||
// Protect against rendering scheduled after the component is unmounted.
|
||||
if (this._unmounted) return;
|
||||
|
||||
// Avoid killing the CPU by limiting the rendering calls.
|
||||
if (this.fpsLimiter.shouldUpdate()) {
|
||||
this.backgroundColor.render();
|
||||
this.viewPosition.render();
|
||||
this.grid.render();
|
||||
this.instancesRenderer.render();
|
||||
this.highlightedInstance.render();
|
||||
this.selectedInstances.render();
|
||||
this.selectionRectangle.render();
|
||||
this.windowBorder.render();
|
||||
this.windowMask.render();
|
||||
this.pixiRenderer.render(this.pixiContainer);
|
||||
}
|
||||
this.backgroundColor.render();
|
||||
this.viewPosition.render();
|
||||
this.grid.render();
|
||||
this.instancesRenderer.render();
|
||||
this.highlightedInstance.render();
|
||||
this.selectedInstances.render();
|
||||
this.selectionRectangle.render();
|
||||
this.windowBorder.render();
|
||||
this.windowMask.render();
|
||||
this.pixiRenderer.render(this.pixiContainer);
|
||||
this.nextFrame = requestAnimationFrame(this._renderScene);
|
||||
};
|
||||
|
||||
|
@@ -10,7 +10,7 @@ const AddLayerRow = ({ onAdd }) => (
|
||||
<TableRowColumn />
|
||||
<TableRowColumn style={styles.visibleColumn} />
|
||||
<TableRowColumn style={styles.toolColumn}>
|
||||
<IconButton onClick={onAdd}>
|
||||
<IconButton onTouchTap={onAdd}>
|
||||
<Add />
|
||||
</IconButton>
|
||||
</TableRowColumn>
|
||||
|
@@ -30,17 +30,17 @@ export default class VariablesEditorDialog extends Component {
|
||||
label="Cancel"
|
||||
secondary={true}
|
||||
keyboardFocused={true}
|
||||
onClick={() => this.props.onClose(false)}
|
||||
onTouchTap={() => this.props.onClose(false)}
|
||||
/>,
|
||||
<FlatButton
|
||||
label="Remove objects"
|
||||
secondary={true}
|
||||
onClick={() => this.props.onClose(true, null)}
|
||||
onTouchTap={() => this.props.onClose(true, null)}
|
||||
/>,
|
||||
<FlatButton
|
||||
label="Move objects"
|
||||
primary={true}
|
||||
onClick={() => this.props.onClose(true, this.state.selectedLayer)}
|
||||
onTouchTap={() => this.props.onClose(true, this.state.selectedLayer)}
|
||||
/>,
|
||||
];
|
||||
|
||||
|
@@ -6,22 +6,20 @@ import VisibilityOff from 'material-ui/svg-icons/action/visibility-off';
|
||||
import IconButton from 'material-ui/IconButton';
|
||||
import Delete from 'material-ui/svg-icons/action/delete';
|
||||
import TextField from 'material-ui/TextField';
|
||||
import muiThemeable from 'material-ui/styles/muiThemeable';
|
||||
import DragHandle from '../UI/DragHandle';
|
||||
import styles from './styles';
|
||||
|
||||
const ThemableLayerRow = ({
|
||||
const LayerRow = ({
|
||||
layerName,
|
||||
nameError,
|
||||
onBlur,
|
||||
onRemove,
|
||||
isVisible,
|
||||
onChangeVisibility,
|
||||
muiTheme,
|
||||
}) => (
|
||||
<TableRow
|
||||
style={{
|
||||
backgroundColor: muiTheme.list.itemsBackgroundColor,
|
||||
backgroundColor: 'white',
|
||||
}}
|
||||
>
|
||||
<TableRowColumn style={styles.handleColumn}>
|
||||
@@ -45,14 +43,11 @@ const ThemableLayerRow = ({
|
||||
/>
|
||||
</TableRowColumn>
|
||||
<TableRowColumn style={styles.toolColumn}>
|
||||
<IconButton onClick={onRemove}>
|
||||
<IconButton onTouchTap={onRemove}>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
</TableRowColumn>
|
||||
</TableRow>
|
||||
);
|
||||
|
||||
const LayerRow = muiThemeable()(
|
||||
ThemableLayerRow
|
||||
);
|
||||
export default LayerRow;
|
||||
|
@@ -1,4 +1,3 @@
|
||||
//TODO: Factor with styles.js from PointsEditor.
|
||||
export default {
|
||||
handleColumn: {
|
||||
width: 24,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user