Add BBText object (#1247)

* Uses `pixi-multistyle-text`
* Add standalone example
* Update the Yarn Dialogue example
This commit is contained in:
Todor Imreorov
2019-12-09 22:54:56 +00:00
committed by Florian Rival
parent 0c045d6e79
commit 2809f53af8
18 changed files with 2271 additions and 82 deletions

View File

@@ -0,0 +1,611 @@
/**
* This is a declaration of an extension for GDevelop 5.
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change
* to this extension file or to any other *.js file that you reference inside.
*
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
* ⚠️ If you make a change and the extension is not loaded, open the developer console
* and search for any errors.
*
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
module.exports = {
createExtension: function(_, gd) {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
'BBText',
_('BBCode Text Object'),
_(
'Displays a rich text label using BBCode markup (allowing to set parts of the text as bold, italic, use different colors and shadows).'
),
'Todor Imreorov',
'Open source (MIT License)'
)
.setExtensionHelpPath('/objects/bbtext_object');
var objectBBText = new gd.ObjectJsImplementation();
objectBBText.updateProperty = function(
objectContent,
propertyName,
newValue
) {
if (propertyName in objectContent) {
if (typeof objectContent[propertyName] === 'boolean')
objectContent[propertyName] = newValue === '1';
else objectContent[propertyName] = newValue;
return true;
}
return false;
};
objectBBText.getProperties = function(objectContent) {
var objectProperties = new gd.MapStringPropertyDescriptor();
objectProperties.set(
'text',
new gd.PropertyDescriptor(objectContent.text)
.setType('textarea')
.setLabel(_('BBCode text'))
);
objectProperties.set(
'color',
new gd.PropertyDescriptor(objectContent.color)
.setType('color')
.setLabel(_('Base color'))
);
objectProperties.set(
'opacity',
new gd.PropertyDescriptor(objectContent.opacity.toString())
.setType('number')
.setLabel(_('Opacity (0-255)'))
);
objectProperties.set(
'fontSize',
new gd.PropertyDescriptor(objectContent.fontSize)
.setType('number')
.setLabel(_('Base size'))
);
objectProperties.set(
'align',
new gd.PropertyDescriptor(objectContent.align)
.setType('choice')
.addExtraInfo('left')
.addExtraInfo('center')
.addExtraInfo('right')
.setLabel(_('Base alignment'))
);
objectProperties.set(
'fontFamily',
new gd.PropertyDescriptor(objectContent.fontFamily)
.setType('string')
.setLabel(_('Base font family'))
);
objectProperties.set(
'wordWrap',
new gd.PropertyDescriptor(objectContent.wordWrap ? 'true' : 'false')
.setType('boolean')
.setLabel(_('Word wrapping'))
);
objectProperties.set(
'visible',
new gd.PropertyDescriptor(objectContent.visible ? 'true' : 'false')
.setType('boolean')
.setLabel(_('Visible on start'))
);
return objectProperties;
};
objectBBText.setRawJSONContent(
JSON.stringify({
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',
visible: true,
color: '#000000',
fontFamily: 'Arial',
align: 'left',
wordWrap: true,
})
);
objectBBText.updateInitialInstanceProperty = function(
objectContent,
instance,
propertyName,
newValue,
project,
layout
) {
return false;
};
objectBBText.getInitialInstanceProperties = function(
content,
instance,
project,
layout
) {
var instanceProperties = new gd.MapStringPropertyDescriptor();
return instanceProperties;
};
const object = extension
.addObject(
'BBText',
_('BBText'),
_(
'Displays a rich text label using BBCode markup (allowing to set parts of the text as bold, italic, use different colors and shadows).'
),
'JsPlatform/Extensions/bbcode32.png',
objectBBText
)
.setIncludeFile('Extensions/BBText/bbtextruntimeobject.js')
.addIncludeFile('Extensions/BBText/bbtextruntimeobject-pixi-renderer.js')
.addIncludeFile(
'Extensions/BBText/pixi-multistyle-text/dist/pixi-multistyle-text.umd.js'
);
/**
* Utility function to add both a setter and a getter to a property from a list.
* Useful for setting multiple generic properties.
*/
const addSettersAndGettersToObject = (gdObject, properties, objectName) => {
properties.forEach(property => {
const parameterType =
property.type === 'boolean' ? 'yesorno' : property.type;
// Add the expression
if (parameterType === 'number') {
gdObject
.addExpression(
`Get${property.functionName}`,
property.expressionDescription,
property.expressionSentence,
'',
'',
property.iconPath,
property.iconPath
)
.addParameter('object', objectName, objectName, false)
.getCodeExtraInformation()
.setFunctionName(`get${property.functionName}`);
} else if (parameterType === 'string') {
gdObject
.addStrExpression(
`Get${property.functionName}`,
property.expressionDescription,
property.expressionSentence,
'',
'',
property.iconPath,
property.iconPath
)
.addParameter('object', objectName, objectName, false)
.getCodeExtraInformation()
.setFunctionName(`get${property.functionName}`);
}
// Add the action
if (parameterType === 'number' || parameterType === 'string') {
const expressionType =
parameterType === 'number' ? 'expression' : 'string';
gdObject
.addAction(
`Set${property.functionName}`,
property.paramLabel,
property.actionDescription,
property.actionSentence,
'',
property.iconPath,
property.iconPath
)
.addParameter('object', objectName, objectName, false)
.addParameter('operator', _("Modification's sign"), '', false)
.addParameter(expressionType, property.paramLabel, '', false)
.getCodeExtraInformation()
.setFunctionName(`set${property.functionName}`)
.setManipulatedType(parameterType)
.setGetter(`get${property.functionName}`);
} else {
gdObject
.addAction(
`Set${property.functionName}`,
property.paramLabel,
property.actionDescription,
property.actionSentence,
'',
property.iconPath,
property.iconPath
)
.addParameter('object', objectName, objectName, false)
.addParameter(
parameterType,
property.paramLabel,
property.options
? '["' + property.options.join('", "') + '"]'
: '',
false
)
.getCodeExtraInformation()
.setFunctionName(`set${property.functionName}`)
.setGetter(`get${property.functionName}`);
}
// Add condition
if (parameterType === 'string' || parameterType === 'number') {
const propExpressionType =
parameterType === 'string' ? 'string' : 'expression';
gdObject
.addCondition(
`Is${property.functionName}`,
property.paramLabel,
property.conditionDescription,
property.conditionSentence,
'',
property.iconPath,
property.iconPath
)
.addParameter('object', objectName, objectName, false)
.addParameter(
'relationalOperator',
_('Sign of the test'),
'',
false
)
.addParameter(
propExpressionType,
parameterType + _(' value'),
'',
false
)
.getCodeExtraInformation()
.setFunctionName(`get${property.functionName}`)
.setManipulatedType(parameterType);
} else if (parameterType === 'yesorno') {
gdObject
.addCondition(
`Is${property.functionName}`,
property.paramLabel,
property.conditionDescription,
property.conditionSentence,
'',
property.iconPath,
property.iconPath
)
.addParameter('object', objectName, objectName, false)
.getCodeExtraInformation()
.setFunctionName(`get${property.functionName}`);
}
});
};
const setterAndGetterProperties = [
{
functionName: 'BBText',
iconPath: 'res/actions/text24.png',
type: 'string',
paramLabel: _('BBCode text'),
conditionDescription: _('Compare the value of the BBCode text.'),
conditionSentence: _('The BBCode text of _PARAM0_ is _PARAM1__PARAM2_'),
actionDescription: _('Set BBCode text'),
actionSentence: _('Do _PARAM1__PARAM2_ to the BBCode text of _PARAM0_'),
expressionDescription: _('Get BBCode text'),
expressionSentence: _('Get BBCode text'),
},
{
functionName: 'Color',
iconPath: 'res/actions/color24.png',
type: 'color',
paramLabel: _('Color'),
conditionDescription: '', // No conditions for a "color" property
conditionSentence: '', // No conditions for a "color" property
actionDescription: _('Set base color'),
actionSentence: _('Set base color of _PARAM0_ to _PARAM1_'),
expressionDescription: '', // No expression for a "color" property
expressionSentence: '', // No expression for a "color" property
},
{
functionName: 'Opacity',
iconPath: 'res/actions/opacity24.png',
type: 'number',
paramLabel: _('Opacity'),
conditionDescription: _(
'Compare the value of the base opacity of the text.'
),
conditionSentence: _(
'The base opacity of _PARAM0_ is _PARAM1__PARAM2_'
),
actionDescription: _('Set base opacity'),
actionSentence: _(
'Do _PARAM1__PARAM2_ to the base opacity of _PARAM0_'
),
expressionDescription: _('Get the base opacity'),
expressionSentence: _('Get the base opacity'),
},
{
functionName: 'FontSize',
iconPath: 'res/actions/characterSize24.png',
type: 'number',
paramLabel: _('Font size'),
conditionDescription: _('Compare the base font size of the text.'),
conditionSentence: _(
'The base font size of _PARAM0_ is _PARAM1__PARAM2_'
),
actionDescription: _('Set base font size'),
actionSentence: _(
'Do _PARAM1__PARAM2_ to the base font size of _PARAM0_'
),
expressionDescription: _('Get the base font size'),
expressionSentence: _('Get the base font size'),
},
{
functionName: 'FontFamily',
iconPath: 'res/actions/font24.png',
type: 'string',
paramLabel: _('Font family'),
conditionDescription: _('Compare the value of font family'),
conditionSentence: _(
'The base font family of _PARAM0_ is _PARAM1__PARAM2_'
),
actionDescription: _('Set font family'),
actionSentence: _(
'Do _PARAM1__PARAM2_ to the base font family of _PARAM0_'
),
expressionDescription: _('Get the base font family'),
expressionSentence: _('Get the base font family'),
},
{
functionName: 'Alignment',
iconPath: 'res/actions/textAlign24.png',
type: 'stringWithSelector',
paramLabel: _('Alignment'),
options: ['left', 'right', 'center'],
conditionDescription: _('Check the current text alignment'),
conditionSentence: _('The text alignment of _PARAM0_ is _PARAM1_'),
actionDescription: _('Change the alignment of the text.'),
actionSentence: _('Set text alignment of _PARAM0_ to _PARAM1_'),
expressionDescription: _('Get the text alignment'),
expressionSentence: _('Get the text alignment'),
},
{
functionName: 'WordWrap',
iconPath: 'res/actions/scaleWidth24.png',
type: 'boolean',
paramLabel: _('Word wrap'),
conditionDescription: _('Check if word wrap is enabled'),
conditionSentence: _('Word wrap is enabled'),
actionDescription: _('Set word wrap'),
actionSentence: _('Activate word wrap for _PARAM0_: _PARAM1_'),
expressionDescription: '',
expressionSentence: '',
},
{
functionName: 'WrappingWidth',
iconPath: 'res/actions/scaleWidth24.png',
type: 'number',
paramLabel: _('Wrapping width'),
conditionDescription: _(
'Compare the width, in pixels, after which the text is wrapped on next line.'
),
conditionSentence: _(
'The wrapping width of _PARAM0_ is _PARAM1__PARAM2_'
),
actionDescription: _(
'Change the width, in pixels, after which the text is wrapped on next line.'
),
actionSentence: _(
'Do _PARAM1__PARAM2_ to the wrapping width of _PARAM0_'
),
expressionDescription: _('Get the wrapping width'),
expressionSentence: _('Get the wrapping width'),
},
];
addSettersAndGettersToObject(object, setterAndGetterProperties, 'BBText');
return extension;
},
/**
* You can optionally add sanity tests that will check the basic working
* of your extension behaviors/objects by instanciating behaviors/objects
* and setting the property to a given value.
*
* If you don't have any tests, you can simply return an empty array like this:
* `runExtensionSanityTests: function(gd, extension) { return []; }`
*
* But it is recommended to create tests for the behaviors/objects properties you created
* to avoid mistakes.
*/
runExtensionSanityTests: function(gd, extension) {
return [];
},
/**
* Register editors for objects.
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerEditorConfigurations: function(objectsEditorService) {
objectsEditorService.registerEditorConfiguration(
'BBText::BBText',
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
helpPagePath: '/objects/bbtext_object',
})
);
},
/**
* Register renderers for instance of objects on the scene editor.
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
*/
registerInstanceRenderers: function(objectsRenderingService) {
const RenderedInstance = objectsRenderingService.RenderedInstance;
const PIXI = objectsRenderingService.PIXI;
const MultiStyleText = objectsRenderingService.requireModule(
__dirname,
'pixi-multistyle-text/dist/pixi-multistyle-text.umd'
);
/**
* Renderer for instances of BBText inside the IDE.
*
* @extends RenderedBBTextInstance
* @class RenderedBBTextInstance
* @constructor
*/
function RenderedBBTextInstance(
project,
layout,
instance,
associatedObject,
pixiContainer,
pixiResourcesLoader
) {
RenderedInstance.call(
this,
project,
layout,
instance,
associatedObject,
pixiContainer,
pixiResourcesLoader
);
const bbTextStyles = {
default: {
fontFamily: 'Arial',
fontSize: '24px',
fill: '#cccccc',
tagStyle: 'bbcode',
wordWrap: true,
wordWrapWidth: 250, // This value is the default wrapping width of the runtime object.
align: 'left',
},
};
this._pixiObject = new MultiStyleText('', bbTextStyles);
this._pixiObject.anchor.x = 0.5;
this._pixiObject.anchor.y = 0.5;
this._pixiContainer.addChild(this._pixiObject);
this.update();
}
RenderedBBTextInstance.prototype = Object.create(
RenderedInstance.prototype
);
/**
* Return the path to the thumbnail of the specified object.
*/
RenderedBBTextInstance.getThumbnail = function(
project,
resourcesLoader,
object
) {
return 'JsPlatform/Extensions/bbcode24.png';
};
/**
* This is called to update the PIXI object on the scene editor
*/
RenderedBBTextInstance.prototype.update = function() {
const rawText = this._associatedObject
.getProperties(this.project)
.get('text')
.getValue();
if (rawText !== this._pixiObject.text) {
this._pixiObject.setText(rawText);
}
const opacity = this._associatedObject
.getProperties(this.project)
.get('opacity')
.getValue();
this._pixiObject.alpha = opacity / 255;
const color = this._associatedObject
.getProperties(this.project)
.get('color')
.getValue();
this._pixiObject.textStyles.default.fill = color;
const fontSize = this._associatedObject
.getProperties(this.project)
.get('fontSize')
.getValue();
this._pixiObject.textStyles.default.fontSize = `${fontSize}px`;
const fontFamily = this._associatedObject
.getProperties(this.project)
.get('fontFamily')
.getValue();
this._pixiObject.textStyles.default.fontFamily = fontFamily;
const wordWrap = this._associatedObject
.getProperties(this.project)
.get('wordWrap')
.getValue();
if (wordWrap !== this._pixiObject._style.wordWrap) {
this._pixiObject._style.wordWrap = wordWrap === 'true';
this._pixiObject.dirty = true;
}
const align = this._associatedObject
.getProperties(this.project)
.get('align')
.getValue();
if (align !== this._pixiObject._style.align) {
this._pixiObject._style.align = align;
this._pixiObject.dirty = true;
}
this._pixiObject.position.x =
this._instance.getX() + this._pixiObject.width / 2;
this._pixiObject.position.y =
this._instance.getY() + this._pixiObject.height / 2;
this._pixiObject.rotation = RenderedInstance.toRad(
this._instance.getAngle()
);
if (this._instance.hasCustomSize() && this._pixiObject) {
const customWidth = this._instance.getCustomWidth();
if (
this._pixiObject &&
this._pixiObject.textStyles.default.wordWrapWidth !== customWidth
) {
this._pixiObject._style.wordWrapWidth = customWidth;
this._pixiObject.dirty = true;
}
}
};
/**
* Return the width of the instance, when it's not resized.
*/
RenderedBBTextInstance.prototype.getDefaultWidth = function() {
return this._pixiObject.width;
};
/**
* Return the height of the instance, when it's not resized.
*/
RenderedBBTextInstance.prototype.getDefaultHeight = function() {
return this._pixiObject.height;
};
objectsRenderingService.registerInstanceRenderer(
'BBText::BBText',
RenderedBBTextInstance
);
},
};

View File

@@ -0,0 +1,112 @@
/**
* The PIXI.js renderer for the BBCode Text runtime object.
*
* @class BBTextRuntimeObjectPixiRenderer
* @constructor
* @param {gdjs.BBTextRuntimeObject} runtimeObject The object to render
* @param {gdjs.RuntimeScene} runtimeScene The gdjs.RuntimeScene in which the object is
*/
gdjs.BBTextRuntimeObjectPixiRenderer = function(runtimeObject, runtimeScene) {
this._object = runtimeObject;
// Load (or reset) the text
if (this._pixiObject === undefined) {
this._pixiObject = new MultiStyleText(runtimeObject._text, {
default: {
fontFamily: runtimeObject._fontFamily,
fontSize: runtimeObject._fontSize + 'px',
fill: runtimeObject._color,
tagStyle: 'bbcode',
wordWrap: runtimeObject._wordWrap,
wordWrapWidth: runtimeObject._wrappingWidth,
align: runtimeObject._align,
},
});
this._object.hidden = !runtimeObject._visible;
} else {
this.updateColor();
this.updateAlignment();
this.updateFontFamily();
this.updateFontSize();
}
runtimeScene
.getLayer('')
.getRenderer()
.addRendererObject(this._pixiObject, runtimeObject.getZOrder());
// Set the anchor in the center, so that the object rotates around
// its center
this._pixiObject.anchor.x = 0.5;
this._pixiObject.anchor.y = 0.5;
this.updateText();
this.updatePosition();
this.updateAngle();
this.updateOpacity();
this.updateVisible();
};
gdjs.BBTextRuntimeObjectRenderer = gdjs.BBTextRuntimeObjectPixiRenderer;
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.getRendererObject = function() {
return this._pixiObject;
};
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateWordWrap = function() {
this._pixiObject._style.wordWrap = this._object._wordWrap;
this._pixiObject.dirty = true;
this.updatePosition();
};
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateWrappingWidth = function() {
this._pixiObject._style.wordWrapWidth = this._object._wrappingWidth;
this._pixiObject.dirty = true;
this.updatePosition();
};
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateText = function() {
this._pixiObject.setText(this._object._text);
this.updatePosition();
};
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateColor = function() {
this._pixiObject.textStyles.default.fill = this._object._color;
};
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateAlignment = function() {
this._pixiObject._style.align = this._object._align;
this._pixiObject.dirty = true;
};
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateFontFamily = function() {
this._pixiObject.textStyles.default.fontFamily = this._object._fontFamily;
};
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateFontSize = function() {
this._pixiObject.textStyles.default.fontSize = this._object._fontSize + 'px';
};
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updatePosition = function() {
this._pixiObject.position.x = this._object.x + this._pixiObject.width / 2;
this._pixiObject.position.y = this._object.y + this._pixiObject.height / 2;
};
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateVisible = function() {
this._pixiObject.hidden = !this._object._visible;
};
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateAngle = function() {
this._pixiObject.rotation = gdjs.toRad(this._object.angle);
};
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.updateOpacity = function() {
this._pixiObject.alpha = this._object._opacity / 255;
};
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.getWidth = function() {
return this._pixiObject.width;
};
gdjs.BBTextRuntimeObjectPixiRenderer.prototype.getHeight = function() {
return this._pixiObject.height;
};

View File

@@ -0,0 +1,200 @@
/**
* Displays a rich text using BBCode markup (allowing to set parts of the text as bold, italic, use different colors and shadows).
* @memberof gdjs
* @class BBTextRuntimeObject
* @extends RuntimeObject
*/
gdjs.BBTextRuntimeObject = function(runtimeScene, objectData) {
gdjs.RuntimeObject.call(this, runtimeScene, objectData);
/** @type number */
this._opacity = objectData.content.opacity;
/** @type boolean */
this._visible = objectData.content.visible;
/** @type string */
this._text = objectData.content.text;
/** @type string */
this._color = objectData.content.color;
/** @type string */
this._fontFamily = objectData.content.fontFamily;
/** @type number */
this._fontSize = objectData.content.fontSize;
/** @type boolean */
this._wordWrap = objectData.content.wordWrap;
/** @type number */
this._wrappingWidth = 250; // This value is the default wrapping width of the runtime object.
/** @type string */
this._align = objectData.content.align;
if (this._renderer)
gdjs.BBTextRuntimeObjectRenderer.call(this._renderer, this, runtimeScene);
else
this._renderer = new gdjs.BBTextRuntimeObjectRenderer(this, runtimeScene);
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
this.onCreated();
};
gdjs.BBTextRuntimeObject.prototype = Object.create(
gdjs.RuntimeObject.prototype
);
gdjs.BBTextRuntimeObject.thisIsARuntimeObjectConstructor = 'BBText::BBText';
gdjs.BBTextRuntimeObject.prototype.getRendererObject = function() {
return this._renderer.getRendererObject();
};
/**
* Initialize the extra parameters that could be set for an instance.
* @private
*/
gdjs.BBTextRuntimeObject.prototype.extraInitializationFromInitialInstance = function(
initialInstanceData
) {
// The wrapping width value (this._wrappingWidth) is using the object's width as an innitial value
if (initialInstanceData.customSize)
this.setWrappingWidth(initialInstanceData.width);
};
gdjs.BBTextRuntimeObject.prototype.onDestroyFromScene = function(runtimeScene) {
gdjs.RuntimeObject.prototype.onDestroyFromScene.call(this, runtimeScene);
};
/**
* Set/Get BBText base style properties
*/
gdjs.BBTextRuntimeObject.prototype.setBBText = function(text) {
this._text = text;
this._renderer.updateText();
};
gdjs.BBTextRuntimeObject.prototype.getBBText = function() {
return this._text;
};
gdjs.BBTextRuntimeObject.prototype.setColor = function(rgbColorString) {
const splitValue = rgbColorString.split(';');
if (splitValue.length !== 3) return;
const hexColor =
'#' +
gdjs.rgbToHex(
parseInt(splitValue[0], 0),
parseInt(splitValue[1], 0),
parseInt(splitValue[2], 0)
);
this._color = hexColor;
this._renderer.updateColor();
};
gdjs.BBTextRuntimeObject.prototype.getColor = function() {
return this._color;
};
gdjs.BBTextRuntimeObject.prototype.setFontSize = function(fontSize) {
this._fontSize = fontSize;
this._renderer.updateFontSize();
};
gdjs.BBTextRuntimeObject.prototype.getFontSize = function() {
return this._fontSize;
};
gdjs.BBTextRuntimeObject.prototype.setFontFamily = function(fontFamily) {
this._fontFamily = fontFamily;
this._renderer.updateFontFamily();
};
gdjs.BBTextRuntimeObject.prototype.getFontFamily = function() {
return this._fontFamily;
};
gdjs.BBTextRuntimeObject.prototype.setAlignment = function(align) {
this._align = align;
this._renderer.updateAlignment();
};
gdjs.BBTextRuntimeObject.prototype.getAlignment = function() {
return this._align;
};
/**
* Set object position on X axis.
* @param {number} x The new position X of the object.
*/
gdjs.BBTextRuntimeObject.prototype.setX = function(x) {
gdjs.RuntimeObject.prototype.setX.call(this, x);
this._renderer.updatePosition();
};
/**
* Set object position on Y axis.
* @param {number} y The new position Y of the object.
*/
gdjs.BBTextRuntimeObject.prototype.setY = function(y) {
gdjs.RuntimeObject.prototype.setY.call(this, y);
this._renderer.updatePosition();
};
/**
* Set the angle of the object.
* @param {number} angle The new angle of the object.
*/
gdjs.BBTextRuntimeObject.prototype.setAngle = function(angle) {
gdjs.RuntimeObject.prototype.setAngle.call(this, angle);
this._renderer.updateAngle();
};
/**
* Set object opacity.
* @param {number} opacity The new opacity of the object (0-255).
*/
gdjs.BBTextRuntimeObject.prototype.setOpacity = function(opacity) {
this._opacity = opacity;
this._renderer.updateOpacity();
};
/**
* Get object opacity.
*/
gdjs.BBTextRuntimeObject.prototype.getOpacity = function() {
return this._opacity;
};
/**
* Set the width.
* @param {number} width The new width in pixels.
*/
gdjs.BBTextRuntimeObject.prototype.setWrappingWidth = function(width) {
this._wrappingWidth = width;
this._renderer.updateWrappingWidth();
};
/**
* Get the wrapping width of the object.
*/
gdjs.BBTextRuntimeObject.prototype.getWrappingWidth = function() {
return this._wrappingWidth;
};
gdjs.BBTextRuntimeObject.prototype.setWordWrap = function(wordWrap) {
this._wordWrap = wordWrap;
this._renderer.updateWordWrap();
};
gdjs.BBTextRuntimeObject.prototype.getWordWrap = function(wordWrap) {
return this._wordWrap;
};
/**
* Get the width of the object.
*/
gdjs.BBTextRuntimeObject.prototype.getWidth = function() {
return this._renderer.getWidth();
};
/**
* Get the height of the object.
*/
gdjs.BBTextRuntimeObject.prototype.getHeight = function() {
return this._renderer.getHeight();
};

View File

@@ -0,0 +1,9 @@
This extension is using release version 0.8.0 (commit 336bed0b206043e2c3e81c373b7ca02094ecabe7) of the pixi-multistyle-text library:
https://github.com/tleunen/pixi-multistyle-text
The BBcode tag feature was especially added for Gdevelop and this extension (commit 2a7be2084598933502c76419d7a86c0e6cd11719)
README:
Add a MultiStyleText object inside pixi.js to easily create text using different styles.
License: MIT

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 748 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 950 B

View File

@@ -120,7 +120,7 @@ export const createPathEditorHeader = ({
headerObject.saveFolderLabel.title =
'Changing the path is disabled on imported GD animations!';
headerObject.setFolderButton.removeEventListener('click', selectFolderPath);
headerObject.setFolderButton.style.visibility = 'hidden';
headerObject.setFolderButton.style.display = 'none';
};
/**
@@ -151,19 +151,26 @@ export const createPathEditorHeader = ({
const render = headerObject => {
const pathEditorVisibility = headerObject.state.visible
? 'visibility:visible'
: 'visibility:hidden;';
? 'display:visible'
: 'display:none;';
headerObject.rightButtons.style = pathEditorVisibility;
headerObject.root.style = pathEditorVisibility;
headerObject.hideButton.textContent = headerObject.state.visible ? '>' : '<';
headerObject.hideButton.textContent = headerObject.state.visible
? '>'
: '...';
headerObject.hideButton.title = headerObject.state.visible
? 'Hide save path options'
: 'Open save path options';
headerObject.hideButton.style = headerObject.state.visible
? 'opacity: 1'
? 'opacity: 1; margin-right: 3px;'
: 'opacity:0.5';
headerObject.root.parentElement.style = headerObject.state.visible
? 'position: relative; display:flex; width:100%'
: 'position: absolute; display:flex; width:100%';
: 'position: absolute; display:flex; width:40px; height:40px; right:0';
headerObject.nameInput.value = headerObject.nameInput.value.replace(
/[^a-zA-Z0-9_-]/g,

View File

@@ -0,0 +1 @@
This shows how to change the bbcode text of a BBText object displayed on the screen.

File diff suppressed because it is too large Load Diff

View File

@@ -13,7 +13,7 @@
"macExecutableFilename": "",
"orientation": "landscape",
"packageName": "com.example.platformer",
"projectFile": "/home/fox/DEV/GDevelop/newIDE/app/resources/examples/yarn-dialogue/yarn-dialogue.json",
"projectFile": "/home/fox/DEV/GDevelop/newIDE/app/resources/examples/dialogue-tree-with-yarn/dialogue-tree-with-yarn.json",
"scaleMode": "linear",
"sizeOnStartupMode": "adaptWidth",
"useExternalSourceFiles": false,
@@ -796,13 +796,6 @@
"smoothed": true,
"userAdded": true
},
{
"file": "dialogueData/npcs.json",
"kind": "json",
"metadata": "",
"name": "dialogueData/npcs.json",
"userAdded": true
},
{
"alwaysLoaded": false,
"file": "NewObject2-1.png",
@@ -811,6 +804,14 @@
"name": "NewObject2-1.png",
"smoothed": true,
"userAdded": false
},
{
"disablePreload": false,
"file": "dialogueData/npcs.json",
"kind": "json",
"metadata": "",
"name": "dialogueData/npcs.json",
"userAdded": false
}
],
"resourceFolders": []
@@ -921,10 +922,10 @@
"name": "avatars"
},
{
"name": "dialogueTextObject"
"name": "questionChoicesText"
},
{
"name": "questionChoicesText"
"name": "DialogueTreeBBtextObject"
}
]
}
@@ -1596,21 +1597,6 @@
"stringProperties": [],
"initialVariables": []
},
{
"angle": 0,
"customSize": true,
"height": 81,
"layer": "GUI",
"locked": false,
"name": "dialogueTextObject",
"width": 533,
"x": 110,
"y": 115,
"zOrder": 43,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 0,
"customSize": false,
@@ -1975,6 +1961,21 @@
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 0,
"customSize": true,
"height": 79,
"layer": "GUI",
"locked": false,
"name": "DialogueTreeBBtextObject",
"width": 543,
"x": 108,
"y": 98,
"zOrder": 47,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
}
],
"objects": [
@@ -5120,25 +5121,6 @@
}
]
},
{
"bold": false,
"italic": false,
"name": "dialogueTextObject",
"smoothed": true,
"tags": "",
"type": "TextObject::Text",
"underlined": false,
"variables": [],
"behaviors": [],
"string": "Text",
"font": "",
"characterSize": 20,
"color": {
"b": 61,
"g": 44,
"r": 30
}
},
{
"bold": false,
"italic": true,
@@ -5375,6 +5357,23 @@
]
}
]
},
{
"name": "DialogueTreeBBtextObject",
"tags": "",
"type": "BBText::BBText",
"variables": [],
"behaviors": [],
"content": {
"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",
"visible": true,
"color": "#000000",
"fontFamily": "Arial",
"align": "left",
"wordWrap": true
}
}
],
"events": [
@@ -5540,7 +5539,7 @@
"colorR": 74,
"creationTime": 0,
"disabled": false,
"folded": true,
"folded": false,
"name": "Other gameplay mechanics",
"source": "",
"type": "BuiltinCommonInstructions::Group",
@@ -5618,16 +5617,6 @@
"textBg"
],
"subInstructions": []
},
{
"type": {
"inverted": false,
"value": "Cache"
},
"parameters": [
"dialogueTextObject"
],
"subInstructions": []
}
],
"events": []
@@ -6559,6 +6548,16 @@
"dialogueData/npcs.json"
],
"subInstructions": []
},
{
"type": {
"inverted": false,
"value": "Cache"
},
"parameters": [
"textHud"
],
"subInstructions": []
}
],
"events": [
@@ -6938,10 +6937,10 @@
{
"type": {
"inverted": false,
"value": "TextObject::String"
"value": "BBText::SetBBText"
},
"parameters": [
"dialogueTextObject",
"DialogueTreeBBtextObject",
"=",
"DialogueTree::ClippedLineText()"
],

View File

@@ -2,20 +2,20 @@
{
"title": "Start",
"tags": "",
"body": "<<avatar sign>> Press Z to interact with the tree critters 🐜🐛🐞 Spacebar to jump. \nAnd welcome to yarn's example GD project! ",
"body": "<<avatar sign>> [color=#e90b0b]Press Z[/color] to interact with the tree critters 🐜🐛🐞 [color=#07a9b0]Spacebar[/color] to jump.\nAnd welcome to yarn's example GD project!",
"position": {
"x": -2074,
"y": 220
"x": -2144,
"y": 468
},
"colorID": 0
},
{
"title": "ant",
"tags": "",
"body": "<<avatar ant>>Please don't bug me now... <<wait 1000>> I am quite busy at the moment.\nHave you seen my crumb?\n[[ yes |antcrumbyes ]] [[ no |antcrumbno ]] ",
"body": "<<avatar ant>>[b]Please don't bug me now[/b]... <<wait 1000>> I am [i]quite[/i] busy at the moment.\nHave you seen my crumb?\n[[ yes |antcrumbyes ]] [[ no |antcrumbno ]] ",
"position": {
"x": -1808,
"y": 222
"x": -1878,
"y": 470
},
"colorID": 0
},
@@ -24,18 +24,18 @@
"tags": "",
"body": "<<if $seenCrumb == true>>\nOh yeah? You saw it on a branch?\nDid you taste it?...\n<<else>>\nOh ha-ha! I can see that you are lying.\n<<endif>>",
"position": {
"x": -2016,
"y": 504
"x": -2086,
"y": 752
},
"colorID": 3
},
{
"title": "antcrumbno",
"tags": "",
"body": "Then scram!\nI don't have time for you...",
"body": "Then [b]scram[/b]!\nI don't have time for you...",
"position": {
"x": -1624,
"y": 496
"x": -1694,
"y": 744
},
"colorID": 0
},
@@ -44,8 +44,8 @@
"tags": "",
"body": "<<avatar crumb>> <<set $seenCrumb to true >> You stumbled on a giant crumb.\nWhat would you like to do with it?\n[[ taste it | eatCrumb]] [[ take it| takeCrumb]] [[ nothing |null ]] ",
"position": {
"x": -1821,
"y": 726
"x": -1891,
"y": 974
},
"colorID": 6
},
@@ -54,8 +54,8 @@
"tags": "",
"body": "Let see <<wait 1000>>.<<wait 200>>.<<wait 200>> .<<wait 200>>these 🥨 are making you thirsty...",
"position": {
"x": -2035,
"y": 977
"x": -2105,
"y": 1225
},
"colorID": 0
},
@@ -64,8 +64,8 @@
"tags": "",
"body": "The thing is way too heavy 😬 ....",
"position": {
"x": -1600,
"y": 964
"x": -1670,
"y": 1212
},
"colorID": 0
},
@@ -74,8 +74,8 @@
"tags": "",
"body": "<<wait 100>> ",
"position": {
"x": -1520,
"y": 727
"x": -1590,
"y": 975
},
"colorID": 0
}

View File

@@ -61,6 +61,13 @@ const jsExtensions = [
extensionModule: require('GDJS-for-web-app-only/Runtime/Extensions/DialogueTree/JsExtension.js'),
objectsRenderingServiceModules: {},
},
{
name: 'BBText',
extensionModule: require('GDJS-for-web-app-only/Runtime/Extensions/BBText/JsExtension.js'),
objectsRenderingServiceModules: {
'pixi-multistyle-text/dist/pixi-multistyle-text.umd': require('GDJS-for-web-app-only/Runtime/Extensions/BBText/pixi-multistyle-text/dist/pixi-multistyle-text.umd'),
},
},
];
type MakeExtensionsLoaderArguments = {|

View File

@@ -122,6 +122,34 @@ export default (
},
getLabel,
};
} else if (valueType === 'color') {
return {
name,
valueType: 'color',
getValue: (instance: Instance): string => {
return getProperties(instance)
.get(name)
.getValue();
},
setValue: (instance: Instance, newValue: string) => {
onUpdateProperty(instance, name, newValue);
},
getLabel,
};
} else if (valueType === 'textarea') {
return {
name,
valueType: 'textarea',
getValue: (instance: Instance): string => {
return getProperties(instance)
.get(name)
.getValue();
},
setValue: (instance: Instance, newValue: string) => {
onUpdateProperty(instance, name, newValue);
},
getLabel,
};
} else {
console.error(
`A property with type=${valueType} could not be mapped to a field. Ensure that this type is correct and understood by the IDE.`

View File

@@ -10,6 +10,9 @@ import FlatButton from '../UI/FlatButton';
import SelectField from '../UI/SelectField';
import SelectOption from '../UI/SelectOption';
import Edit from '@material-ui/icons/Edit';
import ColorField from '../UI/ColorField';
import { hexToRGBColor } from '../Utils/ColorTransformer';
import {
type ResourceKind,
type ResourceSource,
@@ -56,6 +59,18 @@ export type PrimitiveValueField =
getValue: Instance => boolean,
setValue: (instance: Instance, newValue: boolean) => void,
...ValueFieldCommonProperties,
|}
| {|
valueType: 'color',
getValue: Instance => string,
setValue: (instance: Instance, newValue: string) => void,
...ValueFieldCommonProperties,
|}
| {|
valueType: 'textarea',
getValue: Instance => string,
setValue: (instance: Instance, newValue: string) => void,
...ValueFieldCommonProperties,
|};
// "Resource" fields are showing a resource selector.
@@ -209,6 +224,41 @@ export default class PropertiesEditor extends React.Component<Props, {||}> {
disabled={field.disabled}
/>
);
} else if (field.valueType === 'color') {
const { setValue } = field;
return (
<ColorField
key={field.name}
id={field.name}
floatingLabelText={getFieldLabel(this.props.instances, field)}
disableAlpha
fullWidth
color={hexToRGBColor(getFieldValue(this.props.instances, field))}
onChangeComplete={color => {
this.props.instances.forEach(i =>
setValue(i, color.hex || '#000000')
);
this._onInstancesModified(this.props.instances);
}}
/>
);
} else if (field.valueType === 'textarea') {
const { setValue } = field;
return (
<SemiControlledTextField
key={field.name}
id={field.name}
onChange={text => {
this.props.instances.forEach(i => setValue(i, text || ''));
this._onInstancesModified(this.props.instances);
}}
value={getFieldValue(this.props.instances, field)}
floatingLabelText={getFieldLabel(this.props.instances, field)}
floatingLabelFixed
multiLine
style={styles.field}
/>
);
} else {
const { onEditButtonClick, setValue } = field;
return (

View File

@@ -11,3 +11,16 @@ export const rgbToHex = (r: number, g: number, b: number) =>
*/
export const rgbToHexNumber = (r: number, g: number, b: number) =>
(r << 16) + (g << 8) + b;
/**
* Convert a hex color value to an rgb object value.
*/
export const hexToRGBColor = (hex: string) => {
const hexNumber = parseInt(hex.replace('#', ''), 16);
return {
r: (hexNumber >> 16) & 0xff,
g: (hexNumber >> 8) & 0xff,
b: hexNumber & 0xff,
a: 255,
};
};