Compare commits

..

9 Commits

Author SHA1 Message Date
Davy Hélard
71f77964ce Optimize JS events 2025-08-09 16:56:36 +02:00
D8H
1b3734ff6b Add the unit for 3D angle parameter descriptions (#7774) 2025-08-08 17:13:06 +02:00
D8H
6288b30ac3 Add an alert when editing the default variant of an extension from the store (#7773) 2025-08-08 16:39:50 +02:00
D8H
ee435f7081 Fix the tint action of Sprite to handle floating point color components (#7772) 2025-08-08 15:57:19 +02:00
Gleb Volkov
d75b4eb2a9 Add debug flag to GDJS build script (#7771) 2025-08-08 14:17:26 +02:00
Florian Rival
5eeb505807 Fix tests
Don't show in changelog
2025-08-08 11:36:33 +02:00
Florian Rival
30566e35ce Fix changing language not reloading courses in this language 2025-08-08 10:14:57 +02:00
Florian Rival
e058b7f295 Add line height property for Text objects (#7769) 2025-08-07 18:47:02 +02:00
Florian Rival
902a30a9f8 Update capability user-friendly name and fix test
Don't show in changelog
2025-08-07 16:55:52 +02:00
46 changed files with 615 additions and 677 deletions

View File

@@ -18,19 +18,19 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
gd::PlatformExtension& extension) {
extension
.SetExtensionInformation("AnimatableCapability",
_("Objects with animations"),
_("Actions and conditions for objects having animations (sprite, 3D models...)."),
_("Animate objects."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects");
extension.AddInstructionOrExpressionGroupMetadata(_("Animatable capability"))
extension.AddInstructionOrExpressionGroupMetadata(_("Objects with animations"))
.SetIcon("res/actions/animation24.png");
extension.AddInstructionOrExpressionGroupMetadata(_("Animations and images"))
.SetIcon("res/actions/animation24.png");
gd::BehaviorMetadata& aut = extension.AddBehavior(
"AnimatableBehavior",
_("Animatable capability"),
_("Objects with animations"),
"Animation",
_("Actions and conditions for objects having animations (sprite, 3D models...).."),
"",

View File

@@ -18,7 +18,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
gd::PlatformExtension& extension) {
extension
.SetExtensionInformation("EffectCapability",
_("Effect capability"),
_("Objects with effects"),
_("Actions/conditions to enable/disable and change parameters of visual effects applied on objects."),
"Florian Rival",
"Open source (MIT License)")
@@ -28,7 +28,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
gd::BehaviorMetadata& aut = extension.AddBehavior(
"EffectBehavior",
_("Effect capability"),
_("Objects with effects"),
"Effect",
_("Actions/conditions to enable/disable and change parameters of visual effects applied on objects."),
"",

View File

@@ -18,7 +18,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFlippableExtension(
gd::PlatformExtension& extension) {
extension
.SetExtensionInformation("FlippableCapability",
_("Flippable capability"),
_("Flippable objects"),
_("Actions/conditions for objects which can be flipped horizontally or vertically."),
"Florian Rival",
"Open source (MIT License)")
@@ -28,7 +28,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFlippableExtension(
gd::BehaviorMetadata& aut = extension.AddBehavior(
"FlippableBehavior",
_("Flippable capability"),
_("Flippable objects"),
"Flippable",
_("Actions/conditions for objects which can be flipped horizontally or vertically."),
"",

View File

@@ -18,13 +18,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsOpacityExtension(
gd::PlatformExtension& extension) {
extension
.SetExtensionInformation("OpacityCapability",
_("Opacity capability"),
_("Objects with opacity"),
_("Action/condition/expression to change or "
"check the opacity of an object (0-255)."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects");
extension.AddInstructionOrExpressionGroupMetadata(_("Opacity capability"))
extension.AddInstructionOrExpressionGroupMetadata(_("Objects with opacity"))
.SetIcon("res/actions/opacity24.png");
extension.AddInstructionOrExpressionGroupMetadata(_("Visibility"))
.SetIcon("res/actions/opacity24.png");
@@ -32,7 +32,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsOpacityExtension(
gd::BehaviorMetadata& aut =
extension
.AddBehavior("OpacityBehavior",
_("Opacity capability"),
_("Objects with opacity"),
"Opacity",
_("Action/condition/expression to change or check the "
"opacity of an object (0-255)."),

View File

@@ -18,7 +18,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsResizableExtension(
extension
.SetExtensionInformation(
"ResizableCapability",
_("Resizable capability"),
_("Resizable objects"),
_("Change or compare the size (width/height) of an object which can "
"be resized (i.e: most objects)."),
"Florian Rival",
@@ -30,7 +30,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsResizableExtension(
gd::BehaviorMetadata &aut =
extension
.AddBehavior("ResizableBehavior",
_("Resizable capability"),
_("Resizable objects"),
"Resizable",
_("Change or compare the size (width/height) of an "
"object which can be resized (i.e: most objects)."),

View File

@@ -18,13 +18,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
gd::PlatformExtension& extension) {
extension
.SetExtensionInformation("ScalableCapability",
_("Scalable capability"),
_("Scalable objects"),
_("Actions/conditions/expression to change or "
"check the scale of an object (default: 1)."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects");
extension.AddInstructionOrExpressionGroupMetadata(_("Scalable capability"))
extension.AddInstructionOrExpressionGroupMetadata(_("Scalable objects"))
.SetIcon("res/actions/scale24_black.png");
extension.AddInstructionOrExpressionGroupMetadata(_("Size")).SetIcon(
"res/actions/scale24_black.png");
@@ -32,7 +32,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
gd::BehaviorMetadata& aut =
extension
.AddBehavior("ScalableBehavior",
_("Scalable capability"),
_("Scalable objects"),
"Scale",
_("Actions/conditions/expression to change or check the "
"scale of an object (default: 1)."),

View File

@@ -18,17 +18,17 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTextContainerExtension(
gd::PlatformExtension& extension) {
extension
.SetExtensionInformation("TextContainerCapability",
_("Text capability"),
_("Objects containing a text"),
_("Allows an object to contain a text, usually shown on screen, that can be modified."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects");
extension.AddInstructionOrExpressionGroupMetadata(_("Text capability"))
extension.AddInstructionOrExpressionGroupMetadata(_("Objects containing a text"))
.SetIcon("res/conditions/text24_black.png");
gd::BehaviorMetadata& aut = extension.AddBehavior(
"TextContainerBehavior",
_("Text capability"),
_("Objects containing a text"),
"Text",
_("Allows an object to contain a text, usually shown on screen, that can be modified."),
"",

View File

@@ -162,7 +162,12 @@ module.exports = {
)
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.useStandardParameters(
'number',
gd.ParameterOptions.makeNewOptions().setDescription(
_('Angle (in degrees)')
)
)
.setFunctionName('setRotationX')
.setGetter('getRotationX');
@@ -178,7 +183,12 @@ module.exports = {
)
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.useStandardParameters(
'number',
gd.ParameterOptions.makeNewOptions().setDescription(
_('Angle (in degrees)')
)
)
.setFunctionName('setRotationY')
.setGetter('getRotationY');
@@ -196,7 +206,7 @@ module.exports = {
)
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.addParameter('number', _('Rotation angle'), '', false)
.addParameter('number', _('Angle to add (in degrees)'), '', false)
.markAsAdvanced()
.setFunctionName('turnAroundX');
@@ -214,7 +224,7 @@ module.exports = {
)
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.addParameter('number', _('Rotation angle'), '', false)
.addParameter('number', _('Angle to add (in degrees)'), '', false)
.markAsAdvanced()
.setFunctionName('turnAroundY');
@@ -232,7 +242,7 @@ module.exports = {
)
.addParameter('object', _('3D object'), '', false)
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
.addParameter('number', _('Rotation angle'), '', false)
.addParameter('number', _('Angle to add (in degrees)'), '', false)
.markAsAdvanced()
.setFunctionName('turnAroundZ');
}
@@ -594,7 +604,12 @@ module.exports = {
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D model'), 'Model3DObject', false)
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.useStandardParameters(
'number',
gd.ParameterOptions.makeNewOptions().setDescription(
_('Angle (in degrees)')
)
)
.setHidden()
.setFunctionName('setRotationX')
.setGetter('getRotationX');
@@ -611,7 +626,12 @@ module.exports = {
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D model'), 'Model3DObject', false)
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.useStandardParameters(
'number',
gd.ParameterOptions.makeNewOptions().setDescription(
_('Angle (in degrees)')
)
)
.setHidden()
.setFunctionName('setRotationY')
.setGetter('getRotationY');
@@ -630,7 +650,7 @@ module.exports = {
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D model'), 'Model3DObject', false)
.addParameter('number', _('Rotation angle'), '', false)
.addParameter('number', _('Angle to add (in degrees)'), '', false)
.markAsAdvanced()
.setHidden()
.setFunctionName('turnAroundX');
@@ -649,7 +669,7 @@ module.exports = {
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D model'), 'Model3DObject', false)
.addParameter('number', _('Rotation angle'), '', false)
.addParameter('number', _('Angle to add (in degrees)'), '', false)
.markAsAdvanced()
.setHidden()
.setFunctionName('turnAroundY');
@@ -668,7 +688,7 @@ module.exports = {
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D model'), 'Model3DObject', false)
.addParameter('number', _('Rotation angle'), '', false)
.addParameter('number', _('Angle to add (in degrees)'), '', false)
.markAsAdvanced()
.setHidden()
.setFunctionName('turnAroundZ');
@@ -1493,7 +1513,12 @@ module.exports = {
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D cube'), 'Cube3DObject', false)
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.useStandardParameters(
'number',
gd.ParameterOptions.makeNewOptions().setDescription(
_('Angle (in degrees)')
)
)
.setFunctionName('setRotationX')
.setHidden()
.setGetter('getRotationX');
@@ -1510,7 +1535,12 @@ module.exports = {
'res/conditions/3d_box.svg'
)
.addParameter('object', _('3D cube'), 'Cube3DObject', false)
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.useStandardParameters(
'number',
gd.ParameterOptions.makeNewOptions().setDescription(
_('Angle (in degrees)')
)
)
.setFunctionName('setRotationY')
.setHidden()
.setGetter('getRotationY');

View File

@@ -71,14 +71,6 @@ module.exports = {
.setLabel(_('Base size'))
.setGroup(_('Font'));
objectProperties
.getOrCreate('lineHeight')
.setValue((objectContent.lineHeight || 0).toString())
.setType('number')
.setLabel(_('Line height'))
.setGroup(_('Font'))
.setAdvanced(true);
objectProperties
.getOrCreate('align')
.setValue(objectContent.align)
@@ -123,7 +115,6 @@ module.exports = {
text: '[b]bold[/b] [i]italic[/i] [size=15]smaller[/size] [font=times]times[/font] font\n[spacing=12]spaced out[/spacing]\n[outline=yellow]outlined[/outline] [shadow=red]DropShadow[/shadow] ',
opacity: 255,
fontSize: 20,
lineHeight: 0,
visible: true,
color: '0;0;0',
fontFamily: 'Arial',
@@ -359,19 +350,6 @@ module.exports = {
expressionLabel: _('Get the base font size'),
expressionDescription: _('Get the base font size'),
},
{
functionName: 'LineHeight',
iconPath: 'res/actions/textPadding24_black.png',
type: 'number',
instructionLabel: _('Line height'),
paramLabel: _('Line height'),
conditionDescription: _('Compare the line height of the text.'),
conditionSentence: _('the line height'),
actionDescription: _('Set line height'),
actionSentence: _('the line height'),
expressionLabel: _('Get the line height'),
expressionDescription: _('Get the line height'),
},
{
functionName: 'FontFamily',
iconPath: 'res/actions/font24.png',
@@ -550,7 +528,6 @@ module.exports = {
tagStyle: 'bbcode',
wordWrapWidth: 250, // This value is the default wrapping width of the runtime object.
align: 'left',
lineHeight: 0,
},
};
@@ -603,12 +580,6 @@ module.exports = {
this._pixiObject.dirty = true;
}
const lineHeight = object.content.lineHeight || 0;
if (lineHeight !== this._pixiObject.textStyles.default.lineHeight) {
this._pixiObject.textStyles.default.lineHeight = lineHeight;
this._pixiObject.dirty = true;
}
const fontResourceName = object.content.fontFamily;
if (this._fontResourceName !== fontResourceName) {

View File

@@ -32,7 +32,6 @@ namespace gdjs {
wordWrap: runtimeObject._wrapping,
wordWrapWidth: runtimeObject._wrappingWidth,
align: runtimeObject._textAlign as PIXI.TextStyleAlign | undefined,
lineHeight: runtimeObject._lineHeight,
},
});
instanceContainer
@@ -45,7 +44,6 @@ namespace gdjs {
this.updatePosition();
this.updateAngle();
this.updateOpacity();
this.updateLineHeight();
}
getRendererObject() {
@@ -104,13 +102,6 @@ namespace gdjs {
this._pixiObject.dirty = true;
}
updateLineHeight(): void {
//@ts-ignore Private member usage.
this._pixiObject.textStyles.default.lineHeight = this._object._lineHeight;
this._pixiObject.dirty = true;
this.updatePosition();
}
updatePosition(): void {
if (this._object.isWrapping() && this._pixiObject.width !== 0) {
const alignmentX =

View File

@@ -20,7 +20,6 @@ namespace gdjs {
/** Alignment of the text: "left", "center" or "right" */
align: 'left' | 'center' | 'right';
verticalTextAlignment: 'top' | 'center' | 'bottom';
lineHeight?: float;
};
};
export type BBTextObjectData = ObjectData & BBTextObjectDataType;
@@ -36,7 +35,6 @@ namespace gdjs {
align: string;
vta: string;
hidden: boolean;
lh: float;
};
export type BBTextObjectNetworkSyncData = ObjectNetworkSyncData &
@@ -63,7 +61,6 @@ namespace gdjs {
_textAlign: string;
_verticalTextAlignment: string;
_lineHeight: float = 0;
_renderer: gdjs.BBTextRuntimeObjectRenderer;
@@ -90,7 +87,6 @@ namespace gdjs {
this._textAlign = objectData.content.align;
this._verticalTextAlignment =
objectData.content.verticalTextAlignment || 'top';
this._lineHeight = objectData.content.lineHeight || 0;
this.hidden = !objectData.content.visible;
this._renderer = new gdjs.BBTextRuntimeObjectRenderer(
@@ -146,9 +142,6 @@ namespace gdjs {
newObjectData.content.verticalTextAlignment
);
}
if (oldObjectData.content.lineHeight !== newObjectData.content.lineHeight) {
this.setLineHeight(newObjectData.content.lineHeight || 0);
}
return true;
}
@@ -165,7 +158,6 @@ namespace gdjs {
align: this._textAlign,
vta: this._verticalTextAlignment,
hidden: this.hidden,
lh: this._lineHeight,
};
}
@@ -204,9 +196,6 @@ namespace gdjs {
if (this.hidden !== undefined) {
this.hide(networkSyncData.hidden);
}
if (this._lineHeight !== undefined) {
this.setLineHeight(networkSyncData.lh);
}
}
override extraInitializationFromInitialInstance(
@@ -278,16 +267,6 @@ namespace gdjs {
return this._fontFamily;
}
getLineHeight(): number {
return this._lineHeight;
}
setLineHeight(value: float): void {
this._lineHeight = value;
this._renderer.updateLineHeight();
this.invalidateHitboxes();
}
/**
* @deprecated Use `setTextAlignment` instead
*/

View File

@@ -103,14 +103,6 @@ module.exports = {
.setLabel(_('Text scale'))
.setGroup(_('Appearance'));
objectProperties
.getOrCreate('lineHeight')
.setValue((objectContent.lineHeight || 0).toString())
.setType('number')
.setLabel(_('Line height'))
.setGroup(_('Appearance'))
.setAdvanced(true);
objectProperties
.getOrCreate('tint')
.setValue(objectContent.tint)
@@ -125,7 +117,6 @@ module.exports = {
opacity: 255,
scale: 1,
fontSize: 20,
lineHeight: 0,
tint: '255;255;255',
bitmapFontResourceName: '',
textureAtlasResourceName: '',
@@ -422,21 +413,6 @@ module.exports = {
.setFunctionName('setWrappingWidth')
.setGetter('getWrappingWidth');
object
.addExpressionAndConditionAndAction(
'number',
'LineHeight',
_('Line height'),
_('the line height of the text'),
_('the line height'),
'',
'res/actions/textPadding24_black.png'
)
.addParameter('object', _('Bitmap text'), 'BitmapTextObject', false)
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.setFunctionName('setLineHeight')
.setGetter('getLineHeight');
return extension;
},
@@ -698,12 +674,6 @@ module.exports = {
const align = object.content.align;
this._pixiObject.align = align;
const lineHeight = object.content.lineHeight || 0;
if (this._pixiObject.lineHeight !== lineHeight) {
this._pixiObject.lineHeight = lineHeight;
this._pixiObject.dirty = true;
}
const color = object.content.tint;
this._pixiObject.tint =
objectsRenderingService.rgbOrHexToHexNumber(color);

View File

@@ -42,7 +42,6 @@ namespace gdjs {
this.updateScale();
this.updateWrappingWidth();
this.updateTint();
this.updateLineHeight();
}
getRendererObject() {
@@ -135,13 +134,6 @@ namespace gdjs {
this.updatePosition();
}
updateLineHeight(): void {
// @ts-ignore
this._pixiObject.lineHeight = this._object._lineHeight;
this._pixiObject.dirty = true;
this.updatePosition();
}
updateTextContent(): void {
this._pixiObject.text = this._object._text;
this.updatePosition();

View File

@@ -20,8 +20,6 @@ namespace gdjs {
/** Alignment of the text. */
align: 'left' | 'center' | 'right';
verticalTextAlignment: 'top' | 'center' | 'bottom';
/** Line height of the text. */
lineHeight?: float;
};
};
export type BitmapTextObjectData = ObjectData & BitmapTextObjectDataType;
@@ -37,7 +35,6 @@ namespace gdjs {
wwidth: float;
align: string;
vta: string;
lh: float;
};
export type BitmapTextObjectNetworkSyncData = ObjectNetworkSyncData &
@@ -69,7 +66,6 @@ namespace gdjs {
_wrappingWidth: float;
_textAlign: string;
_verticalTextAlignment: string;
_lineHeight: float = 0;
_renderer: gdjs.BitmapTextRuntimeObjectPixiRenderer;
@@ -96,7 +92,6 @@ namespace gdjs {
this._textAlign = objectData.content.align;
this._verticalTextAlignment =
objectData.content.verticalTextAlignment || 'top';
this._lineHeight = objectData.content.lineHeight || 0;
this._renderer = new gdjs.BitmapTextRuntimeObjectRenderer(
this,
@@ -156,9 +151,6 @@ namespace gdjs {
newObjectData.content.verticalTextAlignment
);
}
if (oldObjectData.content.lineHeight !== newObjectData.content.lineHeight) {
this.setLineHeight(newObjectData.content.lineHeight || 0);
}
return true;
}
@@ -176,7 +168,6 @@ namespace gdjs {
wwidth: this._wrappingWidth,
align: this._textAlign,
vta: this._verticalTextAlignment,
lh: this._lineHeight,
};
}
@@ -215,9 +206,6 @@ namespace gdjs {
if (this._verticalTextAlignment !== undefined) {
this.setVerticalTextAlignment(networkSyncData.vta);
}
if (this._lineHeight !== undefined) {
this.setLineHeight(networkSyncData.lh);
}
}
/**
@@ -403,16 +391,6 @@ namespace gdjs {
return this._opacity;
}
getLineHeight(): number {
return this._lineHeight;
}
setLineHeight(value: float): void {
this._lineHeight = value;
this._renderer.updateLineHeight();
this.invalidateHitboxes();
}
/**
* Set the wrapping width.
* @param width The new width in pixels.

View File

@@ -389,26 +389,15 @@ namespace gdjs {
this.updatePosition();
}
setColor(rgbColor): void {
const colors = rgbColor.split(';');
if (colors.length < 3) {
return;
}
this._centerSprite.tint = gdjs.rgbToHexNumber(
parseInt(colors[0], 10),
parseInt(colors[1], 10),
parseInt(colors[2], 10)
);
setColor(rgbOrHexColor: string): void {
const tint = gdjs.rgbOrHexStringToNumber(rgbOrHexColor);
this._centerSprite.tint = tint;
for (
let borderCounter = 0;
borderCounter < this._borderSprites.length;
borderCounter++
) {
this._borderSprites[borderCounter].tint = gdjs.rgbToHexNumber(
parseInt(colors[0], 10),
parseInt(colors[1], 10),
parseInt(colors[2], 10)
);
this._borderSprites[borderCounter].tint = tint;
}
this._spritesContainer.cacheAsBitmap = false;
}
@@ -416,11 +405,11 @@ namespace gdjs {
getColor() {
const rgb = new PIXI.Color(this._centerSprite.tint).toRgbArray();
return (
Math.floor(rgb[0] * 255) +
Math.round(rgb[0] * 255) +
';' +
Math.floor(rgb[1] * 255) +
Math.round(rgb[1] * 255) +
';' +
Math.floor(rgb[2] * 255)
Math.round(rgb[2] * 255)
);
}

View File

@@ -355,18 +355,6 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.UseStandardOperatorParameters("number",
gd::ParameterOptions::MakeNewOptions());
obj.AddExpressionAndConditionAndAction("number", "LineHeight",
_("Line height"),
_("the line height of the text"),
_("the line height"),
_("Font"),
"res/actions/textPadding24_black.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardParameters(
"number",
gd::ParameterOptions::MakeNewOptions().SetDescription(
_("Line height")));
obj.AddAction("SetTextAlignment",
_("Alignment"),
_("Change the text alignment of a multiline text object."),
@@ -461,6 +449,16 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"), "Text")
.UseStandardParameters("number", gd::ParameterOptions::MakeNewOptions());
obj.AddExpressionAndConditionAndAction("number",
"LineHeight",
_("Line height"),
_("the line height of a text object"),
_("the line height"),
"",
"res/actions/font24.png")
.AddParameter("object", _("Object"), "Text")
.UseStandardParameters("number", gd::ParameterOptions::MakeNewOptions());
// Support for deprecated "Size" actions/conditions:
obj.AddDuplicatedAction("Size", "Text::SetFontSize").SetHidden();
obj.AddDuplicatedCondition("Size", "Text::FontSize").SetHidden();

View File

@@ -62,14 +62,6 @@ class TextObjectJsExtension : public gd::PlatformExtension {
GetAllConditionsForObject("TextObject::Text")["TextObject::Padding"]
.SetFunctionName("getPadding");
GetAllExpressionsForObject("TextObject::Text")["LineHeight"]
.SetFunctionName("getLineHeight");
GetAllConditionsForObject("TextObject::Text")["TextObject::Text::LineHeight"]
.SetFunctionName("getLineHeight");
GetAllActionsForObject("TextObject::Text")["TextObject::Text::SetLineHeight"]
.SetFunctionName("setLineHeight")
.SetGetter("getLineHeight");
GetAllActionsForObject("TextObject::Text")["TextObject::SetTextAlignment"]
.SetFunctionName("setTextAlignment")
.SetGetter("getTextAlignment");
@@ -104,6 +96,14 @@ class TextObjectJsExtension : public gd::PlatformExtension {
.SetFunctionName("setOutlineThickness")
.SetGetter("getOutlineThickness");
GetAllExpressionsForObject("TextObject::Text")["LineHeight"]
.SetFunctionName("getLineHeight");
GetAllConditionsForObject("TextObject::Text")["TextObject::Text::LineHeight"]
.SetFunctionName("getLineHeight");
GetAllActionsForObject("TextObject::Text")["TextObject::Text::SetLineHeight"]
.SetFunctionName("setLineHeight")
.SetGetter("getLineHeight");
GetAllActionsForObject("TextObject::Text")["TextObject::ShowShadow"]
.SetFunctionName("showShadow");
GetAllConditionsForObject("TextObject::Text")["TextObject::Text::IsShadowEnabled"]

View File

@@ -18,17 +18,31 @@ This project is released under the MIT License.
using namespace std;
TextObject::TextObject()
: text("Text"), characterSize(20), fontName(""), smoothed(true),
bold(false), italic(false), underlined(false), color("0;0;0"),
textAlignment("left"), verticalTextAlignment("top"),
isOutlineEnabled(false), outlineThickness(2), outlineColor("255;255;255"),
isShadowEnabled(false), shadowColor("0;0;0"), shadowOpacity(127),
shadowAngle(90), shadowDistance(4), shadowBlurRadius(2), lineHeight(0) {}
: text("Text"),
characterSize(20),
lineHeight(0),
fontName(""),
smoothed(true),
bold(false),
italic(false),
underlined(false),
color("0;0;0"),
textAlignment("left"),
verticalTextAlignment("top"),
isOutlineEnabled(false),
outlineThickness(2),
outlineColor("255;255;255"),
isShadowEnabled(false),
shadowColor("0;0;0"),
shadowOpacity(127),
shadowAngle(90),
shadowDistance(4),
shadowBlurRadius(2) {}
TextObject::~TextObject() {};
bool TextObject::UpdateProperty(const gd::String &propertyName,
const gd::String &newValue) {
bool TextObject::UpdateProperty(const gd::String& propertyName,
const gd::String& newValue) {
if (propertyName == "text") {
text = newValue;
return true;
@@ -37,6 +51,10 @@ bool TextObject::UpdateProperty(const gd::String &propertyName,
characterSize = newValue.To<double>();
return true;
}
if (propertyName == "lineHeight") {
lineHeight = newValue.To<double>();
return true;
}
if (propertyName == "font") {
fontName = newValue;
return true;
@@ -97,10 +115,6 @@ bool TextObject::UpdateProperty(const gd::String &propertyName,
shadowBlurRadius = newValue.To<double>();
return true;
}
if (propertyName == "lineHeight") {
lineHeight = newValue.To<double>();
return true;
}
return false;
}
@@ -120,6 +134,13 @@ std::map<gd::String, gd::PropertyDescriptor> TextObject::GetProperties() const {
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
.SetGroup(_("Font"));
objectProperties["lineHeight"]
.SetValue(gd::String::From(lineHeight))
.SetType("number")
.SetLabel(_("Line height"))
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
.SetGroup(_("Font"));
objectProperties["font"]
.SetValue(fontName)
.SetType("resource")
@@ -155,8 +176,7 @@ std::map<gd::String, gd::PropertyDescriptor> TextObject::GetProperties() const {
.AddChoice("center", _("Center"))
.AddChoice("right", _("Right"))
.SetLabel(_("Alignment"))
.SetDescription(
_("Alignment of the text when multiple lines are displayed"))
.SetDescription(_("Alignment of the text when multiple lines are displayed"))
.SetGroup(_("Font"))
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden);
@@ -170,14 +190,6 @@ std::map<gd::String, gd::PropertyDescriptor> TextObject::GetProperties() const {
.SetGroup(_("Font"))
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden);
objectProperties["lineHeight"]
.SetValue(gd::String::From(lineHeight))
.SetType("number")
.SetLabel(_("Line height"))
.SetGroup(_("Font"))
.SetAdvanced()
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden);
objectProperties["isOutlineEnabled"]
.SetValue(isOutlineEnabled ? "true" : "false")
.SetType("boolean")
@@ -258,25 +270,24 @@ std::map<gd::String, gd::PropertyDescriptor> TextObject::GetProperties() const {
return objectProperties;
}
void TextObject::DoUnserializeFrom(gd::Project &project,
const gd::SerializerElement &element) {
void TextObject::DoUnserializeFrom(gd::Project& project,
const gd::SerializerElement& element) {
// Compatibility with GD <= 5.3.188
// end of compatibility code
bool isLegacy = !element.HasChild("content");
auto &content = isLegacy ? element : element.GetChild("content");
auto& content = isLegacy ? element : element.GetChild("content");
SetFontName(content.GetChild("font", 0, "Font").GetValue().GetString());
SetTextAlignment(content.GetChild("textAlignment").GetValue().GetString());
SetVerticalTextAlignment(
content.GetStringAttribute("verticalTextAlignment", "top"));
SetVerticalTextAlignment(content.GetStringAttribute("verticalTextAlignment", "top"));
SetCharacterSize(content.GetChild("characterSize", 0, "CharacterSize")
.GetValue()
.GetInt());
SetLineHeight(content.GetDoubleAttribute("lineHeight", 0));
smoothed = content.GetBoolAttribute("smoothed");
bold = content.GetBoolAttribute("bold");
italic = content.GetBoolAttribute("italic");
underlined = content.GetBoolAttribute("underlined");
SetLineHeight(content.GetDoubleAttribute("lineHeight", 0));
// Compatibility with GD <= 5.3.188
if (isLegacy) {
@@ -309,7 +320,7 @@ void TextObject::DoUnserializeFrom(gd::Project &project,
}
}
void TextObject::DoSerializeTo(gd::SerializerElement &element) const {
void TextObject::DoSerializeTo(gd::SerializerElement& element) const {
// Allow users to rollback to 5.3.188 or older releases without loosing their
// configuration.
// TODO Remove this in a few releases.
@@ -325,9 +336,9 @@ void TextObject::DoSerializeTo(gd::SerializerElement &element) const {
"r", colorComponents.size() == 3 ? colorComponents[0].To<int>() : 0)
.SetAttribute(
"g", colorComponents.size() == 3 ? colorComponents[1].To<int>() : 0)
.SetAttribute("b", colorComponents.size() == 3
? colorComponents[2].To<int>()
: 0);
.SetAttribute(
"b",
colorComponents.size() == 3 ? colorComponents[2].To<int>() : 0);
element.SetAttribute("smoothed", smoothed);
element.SetAttribute("bold", bold);
element.SetAttribute("italic", italic);
@@ -335,20 +346,19 @@ void TextObject::DoSerializeTo(gd::SerializerElement &element) const {
}
// end of compatibility code
auto &content = element.AddChild("content");
auto& content = element.AddChild("content");
content.AddChild("text").SetValue(GetText());
content.AddChild("font").SetValue(GetFontName());
content.AddChild("textAlignment").SetValue(GetTextAlignment());
content.AddChild("verticalTextAlignment")
.SetValue(GetVerticalTextAlignment());
content.AddChild("verticalTextAlignment").SetValue(GetVerticalTextAlignment());
content.AddChild("characterSize").SetValue(GetCharacterSize());
content.AddChild("lineHeight").SetValue(GetLineHeight());
content.AddChild("color").SetValue(GetColor());
content.SetAttribute("smoothed", smoothed);
content.SetAttribute("bold", bold);
content.SetAttribute("italic", italic);
content.SetAttribute("underlined", underlined);
content.SetAttribute("lineHeight", lineHeight);
content.SetAttribute("isOutlineEnabled", isOutlineEnabled);
content.SetAttribute("outlineThickness", outlineThickness);
@@ -362,6 +372,6 @@ void TextObject::DoSerializeTo(gd::SerializerElement &element) const {
content.SetAttribute("shadowBlurRadius", shadowBlurRadius);
}
void TextObject::ExposeResources(gd::ArbitraryResourceWorker &worker) {
void TextObject::ExposeResources(gd::ArbitraryResourceWorker& worker) {
worker.ExposeFont(fontName);
}

View File

@@ -49,6 +49,12 @@ class GD_EXTENSION_API TextObject : public gd::ObjectConfiguration {
*/
inline double GetCharacterSize() const { return characterSize; };
/** \brief Change the line height. */
inline void SetLineHeight(double value) { lineHeight = value; };
/** \brief Get the line height. */
inline double GetLineHeight() const { return lineHeight; };
/** \brief Return the name of the font resource used for the text.
*/
inline const gd::String& GetFontName() const { return fontName; };
@@ -67,9 +73,6 @@ class GD_EXTENSION_API TextObject : public gd::ObjectConfiguration {
verticalTextAlignment = verticalTextAlignment_;
};
void SetLineHeight(double value) { lineHeight = value; };
double GetLineHeight() const { return lineHeight; };
bool IsBold() const { return bold; };
void SetBold(bool enable) { bold = enable; };
bool IsItalic() const { return italic; };
@@ -123,6 +126,7 @@ class GD_EXTENSION_API TextObject : public gd::ObjectConfiguration {
gd::String text;
double characterSize;
double lineHeight;
gd::String fontName;
bool smoothed;
bool bold, italic, underlined;
@@ -140,5 +144,4 @@ class GD_EXTENSION_API TextObject : public gd::ObjectConfiguration {
double shadowAngle;
double shadowDistance;
double shadowBlurRadius;
double lineHeight;
};

View File

@@ -64,7 +64,6 @@ namespace gdjs {
style.wordWrap = this._object._wrapping;
style.wordWrapWidth = this._object._wrappingWidth;
style.breakWords = true;
style.lineHeight = this._object._lineHeight;
style.stroke = gdjs.rgbToHexNumber(
this._object._outlineColor[0],
this._object._outlineColor[1],
@@ -87,6 +86,7 @@ namespace gdjs {
? style.dropShadowDistance + style.dropShadowBlur
: 0;
style.padding = Math.ceil(this._object._padding + extraPaddingForShadow);
style.lineHeight = this._object._lineHeight;
// Prevent spikey outlines by adding a miter limit
style.miterLimit = 3;

View File

@@ -8,7 +8,6 @@ namespace gdjs {
content: {
/** The size of the characters */
characterSize: number;
lineHeight?: number;
/** The font name */
font: string;
/** Is Bold? */
@@ -23,6 +22,8 @@ namespace gdjs {
text: string;
textAlignment: string;
verticalTextAlignment: string;
/** The line height */
lineHeight: float;
isOutlineEnabled: boolean;
outlineThickness: float;
@@ -44,7 +45,6 @@ namespace gdjs {
str: string;
o: float;
cs: number;
lh: float;
fn: string;
b: boolean;
i: boolean;
@@ -64,6 +64,7 @@ namespace gdjs {
sha: float;
shb: float;
pad: integer;
lh: float;
};
export type TextObjectNetworkSyncData = ObjectNetworkSyncData &
@@ -103,7 +104,8 @@ namespace gdjs {
_shadowAngle: float;
_shadowBlur: float;
_lineHeight: float = 0;
_lineHeight: float;
_padding: integer = 5;
_str: string;
_renderer: gdjs.TextRuntimeObjectRenderer;
@@ -131,7 +133,6 @@ namespace gdjs {
this._str = content.text;
this._textAlign = content.textAlignment || 'left';
this._verticalTextAlignment = content.verticalTextAlignment || 'top';
this._lineHeight = content.lineHeight || 0;
this._isOutlineEnabled = content.isOutlineEnabled;
this._outlineThickness = content.outlineThickness;
@@ -143,6 +144,7 @@ namespace gdjs {
this._shadowDistance = content.shadowDistance;
this._shadowBlur = content.shadowBlurRadius;
this._shadowAngle = content.shadowAngle;
this._lineHeight = content.lineHeight || 0;
this._renderer = new gdjs.TextRuntimeObjectRenderer(
this,
@@ -188,9 +190,6 @@ namespace gdjs {
) {
this.setVerticalTextAlignment(newContent.verticalTextAlignment);
}
if (oldContent.lineHeight !== newContent.lineHeight) {
this.setLineHeight(newContent.lineHeight || 0);
}
if (oldContent.isOutlineEnabled !== newContent.isOutlineEnabled) {
this.setOutlineEnabled(newContent.isOutlineEnabled);
}
@@ -218,6 +217,9 @@ namespace gdjs {
if (oldContent.shadowBlurRadius !== newContent.shadowBlurRadius) {
this.setShadowBlurRadius(newContent.shadowBlurRadius);
}
if ((oldContent.lineHeight || 0) !== (newContent.lineHeight || 0)) {
this.setLineHeight(newContent.lineHeight || 0);
}
return true;
}
@@ -227,7 +229,6 @@ namespace gdjs {
str: this._str,
o: this.opacity,
cs: this._characterSize,
lh: this._lineHeight,
fn: this._fontName,
b: this._bold,
i: this._italic,
@@ -246,6 +247,7 @@ namespace gdjs {
shd: this._shadowDistance,
sha: this._shadowAngle,
shb: this._shadowBlur,
lh: this._lineHeight,
pad: this._padding,
};
}
@@ -263,9 +265,6 @@ namespace gdjs {
if (networkSyncData.cs !== undefined) {
this.setCharacterSize(networkSyncData.cs);
}
if (networkSyncData.lh !== undefined) {
this.setLineHeight(networkSyncData.lh);
}
if (networkSyncData.fn !== undefined) {
this.setFontName(networkSyncData.fn);
}
@@ -323,6 +322,9 @@ namespace gdjs {
if (networkSyncData.shb !== undefined) {
this.setShadowBlurRadius(networkSyncData.shb);
}
if (networkSyncData.lh !== undefined) {
this.setLineHeight(networkSyncData.lh);
}
if (networkSyncData.pad !== undefined) {
this.setPadding(networkSyncData.pad);
}
@@ -455,6 +457,22 @@ namespace gdjs {
this._renderer.updateStyle();
}
/**
* Get the line height of the text.
*/
getLineHeight(): float {
return this._lineHeight;
}
/**
* Set the line height of the text.
* @param value The new line height for the text.
*/
setLineHeight(value: float): void {
this._lineHeight = value;
this._renderer.updateStyle();
}
/**
* Set the name of the resource to use for the font.
* @param fontResourceName The name of the font resource.
@@ -584,16 +602,10 @@ namespace gdjs {
/**
* Change the text color.
* @param colorString color as a "R;G;B" string, for example: "255;0;0"
* @param rgbOrHexColor color as a "R;G;B" string, for example: "255;0;0"
*/
setColor(colorString: string): void {
const color = colorString.split(';');
if (color.length < 3) {
return;
}
this._color[0] = parseInt(color[0], 10);
this._color[1] = parseInt(color[1], 10);
this._color[2] = parseInt(color[2], 10);
setColor(rgbOrHexColor: string): void {
this._color = gdjs.rgbOrHexToRGBColor(rgbOrHexColor);
this._useGradient = false;
this._renderer.updateStyle();
}
@@ -702,18 +714,12 @@ namespace gdjs {
/**
* Set the outline for the text object.
* @param str color as a "R;G;B" string, for example: "255;0;0"
* @param rgbOrHexColor color as a "R;G;B" string, for example: "255;0;0"
* @param thickness thickness of the outline (0 = disabled)
* @deprecated Prefer independent setters.
*/
setOutline(str: string, thickness: number): void {
const color = str.split(';');
if (color.length < 3) {
return;
}
this._outlineColor[0] = parseInt(color[0], 10);
this._outlineColor[1] = parseInt(color[1], 10);
this._outlineColor[2] = parseInt(color[2], 10);
setOutline(rgbOrHexColor: string, thickness: number): void {
this._outlineColor = gdjs.rgbOrHexToRGBColor(rgbOrHexColor);
this._outlineThickness = thickness;
this._renderer.updateStyle();
}
@@ -755,25 +761,19 @@ namespace gdjs {
/**
* Set the shadow for the text object.
* @param str color as a "R;G;B" string, for example: "255;0;0"
* @param rgbOrHexColor color as a "R;G;B" string, for example: "255;0;0"
* @param distance distance between the shadow and the text, in pixels.
* @param blur amount of shadow blur, in pixels.
* @param angle shadow offset direction, in degrees.
* @deprecated Prefer independent setters.
*/
setShadow(
str: string,
rgbOrHexColor: string,
distance: number,
blur: integer,
angle: float
): void {
const color = str.split(';');
if (color.length < 3) {
return;
}
this._shadowColor[0] = parseInt(color[0], 10);
this._shadowColor[1] = parseInt(color[1], 10);
this._shadowColor[2] = parseInt(color[2], 10);
this._shadowColor = gdjs.rgbOrHexToRGBColor(rgbOrHexColor);
this._shadowDistance = distance;
this._shadowBlur = blur;
this._shadowAngle = angle;
@@ -886,61 +886,24 @@ namespace gdjs {
strThirdColor: string,
strFourthColor: string
): void {
const colorFirst = strFirstColor.split(';');
const colorSecond = strSecondColor.split(';');
const colorThird = strThirdColor.split(';');
const colorFourth = strFourthColor.split(';');
this._gradient = [];
if (colorFirst.length == 3) {
this._gradient.push([
parseInt(colorFirst[0], 10),
parseInt(colorFirst[1], 10),
parseInt(colorFirst[2], 10),
]);
if (strFirstColor) {
this._gradient.push(gdjs.rgbOrHexToRGBColor(strFirstColor));
}
if (colorSecond.length == 3) {
this._gradient.push([
parseInt(colorSecond[0], 10),
parseInt(colorSecond[1], 10),
parseInt(colorSecond[2], 10),
]);
if (strSecondColor) {
this._gradient.push(gdjs.rgbOrHexToRGBColor(strSecondColor));
}
if (colorThird.length == 3) {
this._gradient.push([
parseInt(colorThird[0], 10),
parseInt(colorThird[1], 10),
parseInt(colorThird[2], 10),
]);
if (strThirdColor) {
this._gradient.push(gdjs.rgbOrHexToRGBColor(strThirdColor));
}
if (colorFourth.length == 3) {
this._gradient.push([
parseInt(colorFourth[0], 10),
parseInt(colorFourth[1], 10),
parseInt(colorFourth[2], 10),
]);
if (strFourthColor) {
this._gradient.push(gdjs.rgbOrHexToRGBColor(strFourthColor));
}
this._gradientType = strGradientType;
this._useGradient = this._gradient.length > 1 ? true : false;
this._renderer.updateStyle();
}
/**
* Get line height of the text object.
*/
getLineHeight(): number {
return this._lineHeight;
}
/**
* Set line height of the text object.
* @param value The new line height in pixels.
*/
setLineHeight(value: float): void {
this._lineHeight = value;
this._renderer.updateStyle();
this.invalidateHitboxes();
}
/**
* Get padding of the text object.
* @return number of pixels around the text before it gets cropped

View File

@@ -92,28 +92,18 @@ namespace gdjs {
-this._object._yOffset % this._tiledSprite.texture.height;
}
setColor(rgbColor: string): void {
const colors = rgbColor.split(';');
if (colors.length < 3) {
return;
}
this._tiledSprite.tint =
'0x' +
gdjs.rgbToHex(
parseInt(colors[0], 10),
parseInt(colors[1], 10),
parseInt(colors[2], 10)
);
setColor(rgbOrHexColor: string): void {
this._tiledSprite.tint = gdjs.rgbOrHexStringToNumber(rgbOrHexColor);
}
getColor() {
const rgb = new PIXI.Color(this._tiledSprite.tint).toRgbArray();
return (
Math.floor(rgb[0] * 255) +
Math.round(rgb[0] * 255) +
';' +
Math.floor(rgb[1] * 255) +
Math.round(rgb[1] * 255) +
';' +
Math.floor(rgb[2] * 255)
Math.round(rgb[2] * 255)
);
}

View File

@@ -145,6 +145,7 @@ describe('gdjs.TweenRuntimeBehavior', () => {
shadowDistance: 4,
shadowAngle: 90,
shadowBlurRadius: 2,
lineHeight: 0,
},
});
runtimeScene.addObject(object);

View File

@@ -145,6 +145,7 @@ describe('gdjs.TweenRuntimeBehavior', () => {
shadowDistance: 4,
shadowAngle: 90,
shadowBlurRadius: 2,
lineHeight: 0,
},
});
runtimeScene.addObject(object);

View File

@@ -1326,11 +1326,11 @@ namespace gdjs {
lightness
);
owner.setColor(
Math.floor(rgbFromHslColor[0]) +
Math.round(rgbFromHslColor[0]) +
';' +
Math.floor(rgbFromHslColor[1]) +
Math.round(rgbFromHslColor[1]) +
';' +
Math.floor(rgbFromHslColor[2])
Math.round(rgbFromHslColor[2])
);
};
} else {
@@ -1439,12 +1439,11 @@ namespace gdjs {
if (!isColorable(this.owner)) return;
const owner = this.owner;
const rgbFromColor: string[] = owner.getColor().split(';');
if (rgbFromColor.length < 3) return;
const rgbFromColor = gdjs.rgbOrHexToRGBColor(owner.getColor());
const hslFromColor = gdjs.evtTools.tween.rgbToHsl(
parseFloat(rgbFromColor[0]),
parseFloat(rgbFromColor[1]),
parseFloat(rgbFromColor[2])
rgbFromColor[0],
rgbFromColor[1],
rgbFromColor[2]
);
const toH = animateHue ? toHue : hslFromColor[0];
@@ -1474,11 +1473,11 @@ namespace gdjs {
);
owner.setColor(
Math.floor(rgbFromHslColor[0]) +
Math.round(rgbFromHslColor[0]) +
';' +
Math.floor(rgbFromHslColor[1]) +
Math.round(rgbFromHslColor[1]) +
';' +
Math.floor(rgbFromHslColor[2])
Math.round(rgbFromHslColor[2])
);
},

View File

@@ -841,13 +841,23 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
event.GetParameterObjects(),
parentContext.GetCurrentObject());
callingCode += "var objects = [];\n";
for (unsigned int i = 0; i < realObjects.size(); ++i) {
parentContext.ObjectsListNeeded(realObjects[i]);
if (realObjects.size() == 1) {
parentContext.ObjectsListNeeded(realObjects[0]);
callingCode +=
"objects.push.apply(objects," +
codeGenerator.GetObjectListName(realObjects[i], parentContext) +
");\n";
"const objects = " +
codeGenerator.GetObjectListName(realObjects[0], parentContext) +
";\n";
} else {
// Groups are rarely used in JS events so it's fine to make
// allocations.
callingCode += "const objects = [];\n";
for (unsigned int i = 0; i < realObjects.size(); ++i) {
parentContext.ObjectsListNeeded(realObjects[i]);
callingCode += "objects.push.apply(objects," +
codeGenerator.GetObjectListName(realObjects[i],
parentContext) +
");\n";
}
}
}

View File

@@ -26,6 +26,28 @@ The game engine is in the _Runtime_ folder. If you want to work on the engine di
- To launch type checking with TypeScript, run `npm install` and `npm run check-types` in `GDJS` folder.
#### Building GDJS Runtime
To build the GDJS Runtime, run `npm run build` in the `GDJS` folder.
**Build Options:**
- **Production build (default)**: `npm run build` - builds with minification enabled
- **Debug build**: `npm run build -- --debug` - builds without minification for easier debugging
- **Custom output path**: `npm run build -- --out=/path/to/output` - specify custom output directory
**Examples:**
```bash
# Standard production build
npm run build
# Debug build for development (no minification)
npm run build -- --debug
# Debug build with custom output path
npm run build -- --debug --out=./debug-build
```
### GDJS Platform (exporters, code generation...)
Check the [GDJS Platform](https://docs.gdevelop.io/GDJS%20Documentation/index.html) documentation or the [full GDevelop developers documentation](https://docs.gdevelop.io/).

View File

@@ -462,12 +462,12 @@ namespace gdjs {
/**
* @param instanceContainer the container owning the layer
* @param layerName The lighting layer with the ambient color.
* @param rgbColor The color, in RGB format ("128;200;255").
* @param rgbOrHexColor The color, in RGB format ("128;200;255").
*/
export const setLayerAmbientLightColor = function (
instanceContainer: gdjs.RuntimeInstanceContainer,
layerName: string,
rgbColor: string
rgbOrHexColor: string
) {
if (
!instanceContainer.hasLayer(layerName) ||
@@ -475,17 +475,10 @@ namespace gdjs {
) {
return;
}
const colors = rgbColor.split(';');
if (colors.length < 3) {
return;
}
const color = gdjs.rgbOrHexToRGBColor(rgbOrHexColor);
return instanceContainer
.getLayer(layerName)
.setClearColor(
parseInt(colors[0], 10),
parseInt(colors[1], 10),
parseInt(colors[2], 10)
);
.setClearColor(color[0], color[1], color[2]);
};
}
}

View File

@@ -24,19 +24,12 @@ namespace gdjs {
export const setBackgroundColor = function (
runtimeScene: gdjs.RuntimeScene,
rgbColor: string
rgbOrHexColor: string
) {
const colors = rgbColor.split(';');
if (colors.length < 3) {
return;
}
const color = gdjs.rgbOrHexToRGBColor(rgbOrHexColor);
runtimeScene
.getScene()
.setBackgroundColor(
parseInt(colors[0]),
parseInt(colors[1]),
parseInt(colors[2])
);
.setBackgroundColor(color[0], color[1], color[2]);
};
export const getElapsedTimeInSeconds = function (

View File

@@ -12,7 +12,6 @@ namespace gdjs {
const logger = new gdjs.Logger('Engine runtime');
const hexStringRegex = /^(#{0,1}[A-Fa-f0-9]{6})$/;
const shorthandHexStringRegex = /^(#{0,1}[A-Fa-f0-9]{3})$/;
const rgbStringRegex = /^(\d{1,3};\d{1,3};\d{1,3})/;
/**
* Contains functions used by events (this is a convention only, functions can actually
@@ -105,9 +104,9 @@ namespace gdjs {
export const rgbOrHexToRGBColor = function (
value: string
): [number, number, number] {
const rgbColor = extractRGBString(value);
if (rgbColor) {
const splitValue = rgbColor.split(';');
// TODO Add a `result` parameter to allow to reuse the returned array.
if (!value.startsWith('#')) {
const splitValue = value.split(';');
// If a RGB string is provided, return the RGB object.
if (splitValue.length === 3) {
return [
@@ -145,11 +144,11 @@ namespace gdjs {
* @param b Blue
*/
export const rgbToHexNumber = function (
r: integer,
g: integer,
b: integer
r: float,
g: float,
b: float
): integer {
return (r << 16) + (g << 8) + b;
return (Math.round(r) << 16) + (Math.round(g) << 8) + Math.round(b);
};
/**
@@ -192,12 +191,6 @@ namespace gdjs {
return matches[0];
};
export const extractRGBString = (str: string): string | null => {
const matches = str.match(rgbStringRegex);
if (!matches) return null;
return matches[0];
};
/**
* Get a random integer between 0 and max.
* @param max The maximum value (inclusive).

View File

@@ -140,18 +140,18 @@ namespace gdjs {
this._sprite.visible = !this._object.hidden;
}
setColor(rgbOrHexColor): void {
setColor(rgbOrHexColor: string): void {
this._sprite.tint = gdjs.rgbOrHexStringToNumber(rgbOrHexColor);
}
getColor() {
const rgb = new PIXI.Color(this._sprite.tint).toRgbArray();
return (
Math.floor(rgb[0] * 255) +
Math.round(rgb[0] * 255) +
';' +
Math.floor(rgb[1] * 255) +
Math.round(rgb[1] * 255) +
';' +
Math.floor(rgb[2] * 255)
Math.round(rgb[2] * 255)
);
}

View File

@@ -7,6 +7,8 @@ const {
} = require('./lib/runtime-files-list');
const args = require('minimist')(process.argv.slice(2), {
string: ['out'],
boolean: ['debug'],
default: { debug: false }
});
const fs = require('fs').promises;
@@ -52,7 +54,7 @@ shell.mkdir('-p', bundledOutPath);
return build({
sourcemap: true,
entryPoints: [inPath],
minify: true,
minify: !args.debug,
outfile: renameBuiltFile(outPath),
}).catch(() => {
// Error is already logged by esbuild.

View File

@@ -36,11 +36,11 @@ describe('gdjs', function () {
expect(gdjs.rgbOrHexToRGBColor('255;255;300')).to.eql([255, 255, 255]);
expect(gdjs.rgbOrHexToRGBColor('999;12;6')).to.eql([255, 12, 6]);
});
it('should cut rgb values if string too long', function () {
it('should cap rgb values', function () {
expect(gdjs.rgbOrHexToRGBColor('255;255;200456')).to.eql([
255,
255,
200,
255,
]);
});
it('should return components for black if unrecognized input', function () {
@@ -48,7 +48,6 @@ describe('gdjs', function () {
expect(gdjs.rgbOrHexToRGBColor('19819830803')).to.eql([0, 0, 0]);
expect(gdjs.rgbOrHexToRGBColor('Infinity')).to.eql([0, 0, 0]);
expect(gdjs.rgbOrHexToRGBColor('-4564')).to.eql([0, 0, 0]);
expect(gdjs.rgbOrHexToRGBColor('9999;12;6')).to.eql([0, 0, 0]);
});
});
});

View File

@@ -3728,6 +3728,8 @@ interface TextObject {
[Const, Ref] DOMString GetText();
void SetCharacterSize(double size);
double GetCharacterSize();
void SetLineHeight(double value);
double GetLineHeight();
void SetFontName([Const] DOMString string);
[Const, Ref] DOMString GetFontName();
boolean IsBold();

View File

@@ -64,7 +64,7 @@ describe('libGD.js object serialization', function() {
obj.delete();
expect(jsonObject).toBe(
'{"bold":false,"italic":false,"smoothed":true,"underlined":false,"string":"Text of the object, with 官话 characters","font":"","textAlignment":"left","characterSize":20.0,"color":{"b":0,"g":0,"r":0},"content":{"bold":false,"isOutlineEnabled":false,"isShadowEnabled":false,"italic":false,"outlineColor":"255;255;255","outlineThickness":2.0,"shadowAngle":90.0,"shadowBlurRadius":2.0,"shadowColor":"0;0;0","shadowDistance":4.0,"shadowOpacity":127.0,"smoothed":true,"underlined":false,"text":"Text of the object, with 官话 characters","font":"","textAlignment":"left","verticalTextAlignment":"top","characterSize":20.0,"color":"0;0;0"}}'
'{"bold":false,"italic":false,"smoothed":true,"underlined":false,"string":"Text of the object, with 官话 characters","font":"","textAlignment":"left","characterSize":20.0,"color":{"b":0,"g":0,"r":0},"content":{"bold":false,"isOutlineEnabled":false,"isShadowEnabled":false,"italic":false,"outlineColor":"255;255;255","outlineThickness":2.0,"shadowAngle":90.0,"shadowBlurRadius":2.0,"shadowColor":"0;0;0","shadowDistance":4.0,"shadowOpacity":127.0,"smoothed":true,"underlined":false,"text":"Text of the object, with 官话 characters","font":"","textAlignment":"left","verticalTextAlignment":"top","characterSize":20.0,"lineHeight":0.0,"color":"0;0;0"}}'
);
});
});

View File

@@ -2758,6 +2758,8 @@ export class TextObject extends ObjectConfiguration {
getText(): string;
setCharacterSize(size: number): void;
getCharacterSize(): number;
setLineHeight(value: number): void;
getLineHeight(): number;
setFontName(string: string): void;
getFontName(): string;
isBold(): boolean;

View File

@@ -5,6 +5,8 @@ declare class gdTextObject extends gdObjectConfiguration {
getText(): string;
setCharacterSize(size: number): void;
getCharacterSize(): number;
setLineHeight(value: number): void;
getLineHeight(): number;
setFontName(string: string): void;
getFontName(): string;
isBold(): boolean;

View File

@@ -19,6 +19,7 @@ import { type ExtensionItemConfigurationAttribute } from '../EventsFunctionsExte
import SelectField from '../UI/SelectField';
import SelectOption from '../UI/SelectOption';
import Window from '../Utils/Window';
import ScrollView from '../UI/ScrollView';
const gd: libGDevelop = global.gd;
@@ -67,150 +68,154 @@ export default function EventsBasedBehaviorEditor({
return (
<I18n>
{({ i18n }) => (
<ColumnStackLayout expand noMargin>
<DismissableAlertMessage
identifier="events-based-behavior-explanation"
kind="info"
>
<Trans>
This is the configuration of your behavior. Make sure to choose a
proper internal name as it's hard to change it later. Enter a
description explaining what the behavior is doing to the object.
</Trans>
</DismissableAlertMessage>
<TextField
floatingLabelText={<Trans>Internal Name</Trans>}
value={eventsBasedBehavior.getName()}
disabled
fullWidth
/>
<SemiControlledTextField
commitOnBlur
floatingLabelText={<Trans>Name displayed in editor</Trans>}
value={eventsBasedBehavior.getFullName()}
onChange={text => {
eventsBasedBehavior.setFullName(text);
onChange();
}}
fullWidth
/>
<SemiControlledTextField
commitOnBlur
floatingLabelText={<Trans>Description</Trans>}
helperMarkdownText={i18n._(
t`Explain what the behavior is doing to the object. Start with a verb when possible.`
)}
value={eventsBasedBehavior.getDescription()}
onChange={text => {
eventsBasedBehavior.setDescription(text);
onChange();
}}
multiline
fullWidth
rows={3}
/>
<ObjectTypeSelector
floatingLabelText={
<Trans>Object on which this behavior can be used</Trans>
}
project={project}
value={eventsBasedBehavior.getObjectType()}
onChange={(objectType: string) => {
eventsBasedBehavior.setObjectType(objectType);
onChange();
}}
allowedObjectTypes={
allObjectTypes.length === 0
? undefined /* Allow anything as the behavior is not used */
: allObjectTypes.length === 1
? [
'',
allObjectTypes[0],
] /* Allow only the type of the objects using the behavior */
: [
'',
] /* More than one type of object are using the behavior. Only "any object" can be used on this behavior */
}
/>
{allObjectTypes.length > 1 && (
<AlertMessage kind="info">
<Trans>
This behavior is being used by multiple types of objects. Thus,
you can't restrict its usage to any particular object type. All
the object types using this behavior are listed here:
{allObjectTypes.join(', ')}
</Trans>
</AlertMessage>
)}
{isDev && (
<SelectField
floatingLabelText={
<Trans>Visibility in quick customization dialog</Trans>
}
value={eventsBasedBehavior.getQuickCustomizationVisibility()}
onChange={(e, i, valueString: string) => {
// $FlowFixMe
const value: QuickCustomization_Visibility = valueString;
eventsBasedBehavior.setQuickCustomizationVisibility(value);
onChange();
}}
fullWidth
>
<SelectOption
value={gd.QuickCustomization.Default}
label={t`Default (visible)`}
/>
<SelectOption
value={gd.QuickCustomization.Visible}
label={t`Always visible`}
/>
<SelectOption
value={gd.QuickCustomization.Hidden}
label={t`Hidden`}
/>
</SelectField>
)}
<Checkbox
label={<Trans>Private</Trans>}
checked={eventsBasedBehavior.isPrivate()}
onCheck={(e, checked) => {
eventsBasedBehavior.setPrivate(checked);
if (onConfigurationUpdated) onConfigurationUpdated('isPrivate');
onChange();
}}
tooltipOrHelperText={
eventsBasedBehavior.isPrivate() ? (
<Trans>
This behavior won't be visible in the scene and events
editors.
</Trans>
) : (
<Trans>
This behavior will be visible in the scene and events editors.
</Trans>
)
}
/>
{eventsBasedBehavior
.getEventsFunctions()
.getEventsFunctionsCount() === 0 && (
<ScrollView>
<ColumnStackLayout expand noMargin>
<DismissableAlertMessage
identifier="empty-events-based-behavior-explanation"
identifier="events-based-behavior-explanation"
kind="info"
>
<Trans>
Once you're done, start adding some functions to the behavior.
Then, test the behavior by adding it to an object in a scene.
This is the configuration of your behavior. Make sure to choose
a proper internal name as it's hard to change it later. Enter a
description explaining what the behavior is doing to the object.
</Trans>
</DismissableAlertMessage>
)}
<Line noMargin>
<HelpButton
key="help"
helpPagePath="/behaviors/events-based-behaviors"
<TextField
floatingLabelText={<Trans>Internal Name</Trans>}
value={eventsBasedBehavior.getName()}
disabled
fullWidth
/>
</Line>
</ColumnStackLayout>
<SemiControlledTextField
commitOnBlur
floatingLabelText={<Trans>Name displayed in editor</Trans>}
value={eventsBasedBehavior.getFullName()}
onChange={text => {
eventsBasedBehavior.setFullName(text);
onChange();
}}
fullWidth
/>
<SemiControlledTextField
commitOnBlur
floatingLabelText={<Trans>Description</Trans>}
helperMarkdownText={i18n._(
t`Explain what the behavior is doing to the object. Start with a verb when possible.`
)}
value={eventsBasedBehavior.getDescription()}
onChange={text => {
eventsBasedBehavior.setDescription(text);
onChange();
}}
multiline
fullWidth
rows={3}
/>
<ObjectTypeSelector
floatingLabelText={
<Trans>Object on which this behavior can be used</Trans>
}
project={project}
value={eventsBasedBehavior.getObjectType()}
onChange={(objectType: string) => {
eventsBasedBehavior.setObjectType(objectType);
onChange();
}}
allowedObjectTypes={
allObjectTypes.length === 0
? undefined /* Allow anything as the behavior is not used */
: allObjectTypes.length === 1
? [
'',
allObjectTypes[0],
] /* Allow only the type of the objects using the behavior */
: [
'',
] /* More than one type of object are using the behavior. Only "any object" can be used on this behavior */
}
/>
{allObjectTypes.length > 1 && (
<AlertMessage kind="info">
<Trans>
This behavior is being used by multiple types of objects.
Thus, you can't restrict its usage to any particular object
type. All the object types using this behavior are listed
here:
{allObjectTypes.join(', ')}
</Trans>
</AlertMessage>
)}
{isDev && (
<SelectField
floatingLabelText={
<Trans>Visibility in quick customization dialog</Trans>
}
value={eventsBasedBehavior.getQuickCustomizationVisibility()}
onChange={(e, i, valueString: string) => {
// $FlowFixMe
const value: QuickCustomization_Visibility = valueString;
eventsBasedBehavior.setQuickCustomizationVisibility(value);
onChange();
}}
fullWidth
>
<SelectOption
value={gd.QuickCustomization.Default}
label={t`Default (visible)`}
/>
<SelectOption
value={gd.QuickCustomization.Visible}
label={t`Always visible`}
/>
<SelectOption
value={gd.QuickCustomization.Hidden}
label={t`Hidden`}
/>
</SelectField>
)}
<Checkbox
label={<Trans>Private</Trans>}
checked={eventsBasedBehavior.isPrivate()}
onCheck={(e, checked) => {
eventsBasedBehavior.setPrivate(checked);
if (onConfigurationUpdated) onConfigurationUpdated('isPrivate');
onChange();
}}
tooltipOrHelperText={
eventsBasedBehavior.isPrivate() ? (
<Trans>
This behavior won't be visible in the scene and events
editors.
</Trans>
) : (
<Trans>
This behavior will be visible in the scene and events
editors.
</Trans>
)
}
/>
{eventsBasedBehavior
.getEventsFunctions()
.getEventsFunctionsCount() === 0 && (
<DismissableAlertMessage
identifier="empty-events-based-behavior-explanation"
kind="info"
>
<Trans>
Once you're done, start adding some functions to the behavior.
Then, test the behavior by adding it to an object in a scene.
</Trans>
</DismissableAlertMessage>
)}
<Line noMargin>
<HelpButton
key="help"
helpPagePath="/behaviors/events-based-behaviors"
/>
</Line>
</ColumnStackLayout>
</ScrollView>
)}
</I18n>
);

View File

@@ -72,6 +72,7 @@ export default function EventsBasedObjectEditorPanel({
</Line>
{currentTab === 'configuration' && (
<EventsBasedObjectEditor
eventsFunctionsExtension={eventsFunctionsExtension}
eventsBasedObject={eventsBasedObject}
unsavedChanges={unsavedChanges}
onOpenCustomObjectEditor={onOpenCustomObjectEditor}

View File

@@ -6,7 +6,8 @@ import * as React from 'react';
import TextField from '../UI/TextField';
import SemiControlledTextField from '../UI/SemiControlledTextField';
import DismissableAlertMessage from '../UI/DismissableAlertMessage';
import { ColumnStackLayout } from '../UI/Layout';
import AlertMessage from '../UI/AlertMessage';
import { ColumnStackLayout, LineStackLayout } from '../UI/Layout';
import useForceUpdate from '../Utils/UseForceUpdate';
import Checkbox from '../UI/Checkbox';
import HelpButton from '../UI/HelpButton';
@@ -14,12 +15,15 @@ import { Line } from '../UI/Grid';
import { type UnsavedChanges } from '../MainFrame/UnsavedChangesContext';
import RaisedButton from '../UI/RaisedButton';
import Window from '../Utils/Window';
import ScrollView from '../UI/ScrollView';
import { Column } from '../UI/Grid';
const gd: libGDevelop = global.gd;
const isDev = Window.isDev();
type Props = {|
eventsFunctionsExtension: gdEventsFunctionsExtension,
eventsBasedObject: gdEventsBasedObject,
onOpenCustomObjectEditor: () => void,
unsavedChanges?: ?UnsavedChanges,
@@ -29,6 +33,7 @@ type Props = {|
|};
export default function EventsBasedObjectEditor({
eventsFunctionsExtension,
eventsBasedObject,
onOpenCustomObjectEditor,
unsavedChanges,
@@ -47,135 +52,155 @@ export default function EventsBasedObjectEditor({
);
return (
<ColumnStackLayout expand noMargin>
<DismissableAlertMessage
identifier="events-based-object-explanation"
kind="info"
>
<Trans>
This is the configuration of your object. Make sure to choose a proper
internal name as it's hard to change it later.
</Trans>
</DismissableAlertMessage>
<TextField
floatingLabelText={<Trans>Internal Name</Trans>}
value={eventsBasedObject.getName()}
disabled
fullWidth
/>
<SemiControlledTextField
commitOnBlur
floatingLabelText={<Trans>Name displayed in editor</Trans>}
value={eventsBasedObject.getFullName()}
onChange={text => {
eventsBasedObject.setFullName(text);
onChange();
}}
fullWidth
/>
<SemiControlledTextField
commitOnBlur
floatingLabelText={<Trans>Description</Trans>}
floatingLabelFixed
translatableHintText={t`The description of the object should explain what the object is doing, and, briefly, how to use it.`}
value={eventsBasedObject.getDescription()}
onChange={text => {
eventsBasedObject.setDescription(text);
onChange();
}}
multiline
fullWidth
rows={3}
/>
<SemiControlledTextField
commitOnBlur
floatingLabelText={<Trans>Default name for created objects</Trans>}
value={
eventsBasedObject.getDefaultName() || eventsBasedObject.getName()
}
onChange={newName => {
eventsBasedObject.setDefaultName(gd.Project.getSafeName(newName));
onChange();
}}
fullWidth
/>
<Checkbox
label={<Trans>Use 3D rendering</Trans>}
checked={eventsBasedObject.isRenderedIn3D()}
onCheck={(e, checked) => {
eventsBasedObject.markAsRenderedIn3D(checked);
onChange();
}}
/>
<Checkbox
label={<Trans>Has animations</Trans>}
checked={eventsBasedObject.isAnimatable()}
onCheck={(e, checked) => {
eventsBasedObject.markAsAnimatable(checked);
onChange();
}}
/>
<Checkbox
label={<Trans>Contains text</Trans>}
checked={eventsBasedObject.isTextContainer()}
onCheck={(e, checked) => {
eventsBasedObject.markAsTextContainer(checked);
onChange();
}}
/>
<Checkbox
label={<Trans>Expand inner area with parent</Trans>}
checked={eventsBasedObject.isInnerAreaFollowingParentSize()}
onCheck={(e, checked) => {
eventsBasedObject.markAsInnerAreaFollowingParentSize(checked);
onChange();
onEventsBasedObjectChildrenEdited(eventsBasedObject);
}}
/>
{isDev && (
<ScrollView>
<ColumnStackLayout expand noMargin>
<DismissableAlertMessage
identifier="events-based-object-explanation"
kind="info"
>
<Trans>
This is the configuration of your object. Make sure to choose a
proper internal name as it's hard to change it later.
</Trans>
</DismissableAlertMessage>
<TextField
floatingLabelText={<Trans>Internal Name</Trans>}
value={eventsBasedObject.getName()}
disabled
fullWidth
/>
<SemiControlledTextField
commitOnBlur
floatingLabelText={<Trans>Name displayed in editor</Trans>}
value={eventsBasedObject.getFullName()}
onChange={text => {
eventsBasedObject.setFullName(text);
onChange();
}}
fullWidth
/>
<SemiControlledTextField
commitOnBlur
floatingLabelText={<Trans>Description</Trans>}
floatingLabelFixed
translatableHintText={t`The description of the object should explain what the object is doing, and, briefly, how to use it.`}
value={eventsBasedObject.getDescription()}
onChange={text => {
eventsBasedObject.setDescription(text);
onChange();
}}
multiline
fullWidth
rows={3}
/>
<SemiControlledTextField
commitOnBlur
floatingLabelText={<Trans>Default name for created objects</Trans>}
value={
eventsBasedObject.getDefaultName() || eventsBasedObject.getName()
}
onChange={newName => {
eventsBasedObject.setDefaultName(gd.Project.getSafeName(newName));
onChange();
}}
fullWidth
/>
<Checkbox
label={<Trans>Use legacy renderer</Trans>}
checked={eventsBasedObject.isUsingLegacyInstancesRenderer()}
label={<Trans>Use 3D rendering</Trans>}
checked={eventsBasedObject.isRenderedIn3D()}
onCheck={(e, checked) => {
eventsBasedObject.makAsUsingLegacyInstancesRenderer(checked);
eventsBasedObject.markAsRenderedIn3D(checked);
onChange();
}}
/>
<Checkbox
label={<Trans>Has animations</Trans>}
checked={eventsBasedObject.isAnimatable()}
onCheck={(e, checked) => {
eventsBasedObject.markAsAnimatable(checked);
onChange();
}}
/>
<Checkbox
label={<Trans>Contains text</Trans>}
checked={eventsBasedObject.isTextContainer()}
onCheck={(e, checked) => {
eventsBasedObject.markAsTextContainer(checked);
onChange();
}}
/>
<Checkbox
label={<Trans>Expand inner area with parent</Trans>}
checked={eventsBasedObject.isInnerAreaFollowingParentSize()}
onCheck={(e, checked) => {
eventsBasedObject.markAsInnerAreaFollowingParentSize(checked);
onChange();
onEventsBasedObjectChildrenEdited(eventsBasedObject);
}}
/>
)}
<Checkbox
label={<Trans>Private</Trans>}
checked={eventsBasedObject.isPrivate()}
onCheck={(e, checked) => {
eventsBasedObject.setPrivate(checked);
onChange();
onEventsBasedObjectChildrenEdited(eventsBasedObject);
}}
tooltipOrHelperText={
eventsBasedObject.isPrivate() ? (
<Trans>
This object won't be visible in the scene and events editors.
</Trans>
) : (
<Trans>
This object will be visible in the scene and events editors.
</Trans>
)
}
/>
<Line noMargin justifyContent="center">
<RaisedButton
label={<Trans>Open visual editor for the object</Trans>}
primary
onClick={onOpenCustomObjectEditor}
{isDev && (
<Checkbox
label={<Trans>Use legacy renderer</Trans>}
checked={eventsBasedObject.isUsingLegacyInstancesRenderer()}
onCheck={(e, checked) => {
eventsBasedObject.makAsUsingLegacyInstancesRenderer(checked);
onChange();
onEventsBasedObjectChildrenEdited(eventsBasedObject);
}}
/>
)}
<Checkbox
label={<Trans>Private</Trans>}
checked={eventsBasedObject.isPrivate()}
onCheck={(e, checked) => {
eventsBasedObject.setPrivate(checked);
onChange();
onEventsBasedObjectChildrenEdited(eventsBasedObject);
}}
tooltipOrHelperText={
eventsBasedObject.isPrivate() ? (
<Trans>
This object won't be visible in the scene and events editors.
</Trans>
) : (
<Trans>
This object will be visible in the scene and events editors.
</Trans>
)
}
/>
</Line>
<Line noMargin>
<HelpButton
key="help"
helpPagePath="/objects/custom-objects-prefab-template"
/>
</Line>
</ColumnStackLayout>
{eventsFunctionsExtension.getOriginName() ===
'gdevelop-extension-store' ? (
<AlertMessage
kind="error"
renderRightButton={() => (
<RaisedButton
label={<Trans>Edit the default variant</Trans>}
primary
onClick={onOpenCustomObjectEditor}
/>
)}
>
<Trans>
The default variant is erased when the extension is updated.
</Trans>
</AlertMessage>
) : (
<Line noMargin justifyContent="center">
<RaisedButton
label={<Trans>Open visual editor for the object</Trans>}
primary
onClick={onOpenCustomObjectEditor}
/>
</Line>
)}
<Line noMargin>
<HelpButton
key="help"
helpPagePath="/objects/custom-objects-prefab-template"
/>
</Line>
</ColumnStackLayout>
</ScrollView>
);
}

View File

@@ -33,7 +33,7 @@ describe('EnumerateInstructions', () => {
expect.objectContaining({
displayedName: 'Animation finished',
fullGroupName:
'General Animatable capability Animations and images',
'General Objects with animations Animations and images',
type: 'AnimatableCapability::AnimatableBehavior::HasAnimationEnded',
})
);

View File

@@ -545,6 +545,20 @@ const useCourses = () => {
]
);
React.useEffect(
() => {
if (language) {
console.info(
`Resetting course chapters cache as language changed to ${language}.`
);
setChaptersByCourseIdByUserId(() => ({
'': noCourseChapters,
}));
}
},
[language]
);
// This callback will change (triggering re-renders)
// anytime the chapters are fetched for a course for a user.
const getCourseChapters = React.useCallback(

View File

@@ -56,12 +56,12 @@ export default class TextEditor extends React.Component<EditorProps, void> {
resourceManagementProps,
renderObjectNameField,
} = this.props;
const textObjectConfiguration =
gd.asTextObjectConfiguration(objectConfiguration);
const textObjectConfiguration = gd.asTextObjectConfiguration(
objectConfiguration
);
const textAlignment = textObjectConfiguration.getTextAlignment();
const verticalTextAlignment =
textObjectConfiguration.getVerticalTextAlignment();
const verticalTextAlignment = textObjectConfiguration.getVerticalTextAlignment();
return (
<ColumnStackLayout noMargin>
@@ -82,7 +82,7 @@ export default class TextEditor extends React.Component<EditorProps, void> {
margin="none"
style={styles.sizeTextField}
value={textObjectConfiguration.getCharacterSize()}
onChange={(value) => {
onChange={value => {
textObjectConfiguration.setCharacterSize(
parseInt(value, 10) || 0
);
@@ -99,7 +99,7 @@ export default class TextEditor extends React.Component<EditorProps, void> {
textObjectConfiguration.getColor(),
255
)}
onChangeComplete={(color) => {
onChangeComplete={color => {
const rgbString = rgbColorToRGBString(color.rgb);
textObjectConfiguration.setColor(rgbString);
this.forceUpdate();
@@ -211,23 +211,13 @@ export default class TextEditor extends React.Component<EditorProps, void> {
fullWidth
canBeReset
initialResourceName={textObjectConfiguration.getFontName()}
onChange={(resourceName) => {
onChange={resourceName => {
textObjectConfiguration.setFontName(resourceName);
this.forceUpdate();
}}
floatingLabelText={<Trans>Choose a font</Trans>}
hintText={<Trans>Choose a font</Trans>}
/>
<SemiControlledTextField
floatingLabelText={<Trans>Line height</Trans>}
type="number"
fullWidth
value={textObjectConfiguration.getLineHeight()}
onChange={(value) => {
textObjectConfiguration.setLineHeight(parseFloat(value) || 0);
this.forceUpdate();
}}
/>
<Line noMargin>
<SemiControlledTextField
floatingLabelText={<Trans>Initial text to display</Trans>}
@@ -240,7 +230,7 @@ export default class TextEditor extends React.Component<EditorProps, void> {
rows={8}
rowsMax={8}
value={textObjectConfiguration.getText()}
onChange={(value) => {
onChange={value => {
textObjectConfiguration.setText(value);
this.forceUpdate();
this.props.onSizeUpdated();
@@ -269,7 +259,7 @@ export default class TextEditor extends React.Component<EditorProps, void> {
disableAlpha
fullWidth
color={textObjectConfiguration.getOutlineColor()}
onChange={(color) => {
onChange={color => {
const rgbString =
color.length === 0 ? '' : rgbOrHexToRGBString(color);
textObjectConfiguration.setOutlineColor(rgbString);
@@ -283,7 +273,7 @@ export default class TextEditor extends React.Component<EditorProps, void> {
floatingLabelText={<Trans>Thickness</Trans>}
type="number"
value={textObjectConfiguration.getOutlineThickness()}
onChange={(value) => {
onChange={value => {
textObjectConfiguration.setOutlineThickness(
parseInt(value, 10) || 0
);
@@ -314,7 +304,7 @@ export default class TextEditor extends React.Component<EditorProps, void> {
floatingLabelText={<Trans>Distance</Trans>}
type="number"
value={textObjectConfiguration.getShadowDistance()}
onChange={(value) => {
onChange={value => {
textObjectConfiguration.setShadowDistance(
parseInt(value, 10) || 0
);
@@ -328,7 +318,7 @@ export default class TextEditor extends React.Component<EditorProps, void> {
floatingLabelText={<Trans>Angle</Trans>}
type="number"
value={textObjectConfiguration.getShadowAngle()}
onChange={(value) => {
onChange={value => {
textObjectConfiguration.setShadowAngle(
parseInt(value, 10) || 0
);
@@ -344,7 +334,7 @@ export default class TextEditor extends React.Component<EditorProps, void> {
disableAlpha
fullWidth
color={textObjectConfiguration.getShadowColor()}
onChange={(color) => {
onChange={color => {
const rgbString =
color.length === 0 ? '' : rgbOrHexToRGBString(color);
textObjectConfiguration.setShadowColor(rgbString);
@@ -358,7 +348,7 @@ export default class TextEditor extends React.Component<EditorProps, void> {
floatingLabelText={<Trans>Opacity (0 - 255)</Trans>}
type="number"
value={textObjectConfiguration.getShadowOpacity()}
onChange={(value) => {
onChange={value => {
textObjectConfiguration.setShadowOpacity(
parseInt(value, 10) || 0
);
@@ -373,7 +363,7 @@ export default class TextEditor extends React.Component<EditorProps, void> {
floatingLabelText={<Trans>Blur radius</Trans>}
type="number"
value={textObjectConfiguration.getShadowBlurRadius()}
onChange={(value) => {
onChange={value => {
textObjectConfiguration.setShadowBlurRadius(
parseInt(value, 10) || 0
);
@@ -381,6 +371,21 @@ export default class TextEditor extends React.Component<EditorProps, void> {
}}
/>
</Column>
<Text size="block-title" noMargin>
<Trans>Multiline</Trans>
</Text>
<Line noMargin>
<SemiControlledTextField
floatingLabelText={<Trans>Line height</Trans>}
type="number"
fullWidth
value={textObjectConfiguration.getLineHeight()}
onChange={value => {
textObjectConfiguration.setLineHeight(parseFloat(value) || 0);
this.forceUpdate();
}}
/>
</Line>
</ColumnStackLayout>
);
}

View File

@@ -13,7 +13,6 @@ export default class RenderedTextInstance extends RenderedInstance {
_isItalic: boolean = false;
_isBold: boolean = false;
_characterSize: number = 0;
_lineHeight: number = 0;
_wrapping: boolean = false;
_wrappingWidth: number = 0;
_styleFontDirty: boolean = true;
@@ -33,6 +32,7 @@ export default class RenderedTextInstance extends RenderedInstance {
_shadowColor = '0;0;0';
_shadowOpacity = 127;
_shadowBlurRadius = 2;
_lineHeight = 0;
constructor(
project: gdProject,
@@ -170,12 +170,12 @@ export default class RenderedTextInstance extends RenderedInstance {
style.fontSize = Math.max(1, this._characterSize);
style.fontStyle = this._isItalic ? 'italic' : 'normal';
style.fontWeight = this._isBold ? 'bold' : 'normal';
style.lineHeight = this._lineHeight !== 0 ? this._lineHeight : undefined;
style.fill = rgbStringToHexNumber(this._color);
style.wordWrap = this._wrapping;
style.wordWrapWidth = this._wrappingWidth <= 1 ? 1 : this._wrappingWidth;
style.breakWords = true;
style.align = this._textAlignment;
style.lineHeight = this._lineHeight;
style.stroke = rgbStringToHexNumber(this._outlineColor);
style.strokeThickness = this._isOutlineEnabled

View File

@@ -104,6 +104,7 @@ export class ExtensionTreeViewItemContent implements TreeViewItemContent {
if (oldName === newName) {
return;
}
this.eventsFunctionsExtension.setOrigin('', '');
this.props.onRenameEventsFunctionsExtension(oldName, newName);
}
@@ -219,6 +220,9 @@ export class ExtensionTreeViewItemContent implements TreeViewItemContent {
project
);
newEventsFunctionsExtension.setName(newName); // Unserialization has overwritten the name.
if (newName !== name) {
newEventsFunctionsExtension.setOrigin('', '');
}
this._onProjectItemModified();
this.props.onReloadEventsFunctionsExtensions();

View File

@@ -17,6 +17,7 @@ export default {
export const Default = () => (
<EventsBasedObjectEditor
eventsFunctionsExtension={testProject.testEventsFunctionsExtension}
eventsBasedObject={testProject.testEventsBasedObject}
onOpenCustomObjectEditor={action('onOpenCustomObjectEditor')}
onEventsBasedObjectChildrenEdited={action(