diff --git a/src/core/const.js b/src/core/const.js index a2003c3..470852b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -187,6 +187,8 @@ * If set to DEFAULT, the renderer will occasianally check textures usage. If they are not used for a specified period of time they will be removed from the GPU. * They will of corse be uploaded again when they are required. This is a silent behind the scenes process that should ensure that the GPU does not get filled up. * Handy for mobile devices! + * This property only affects WebGL + * Handy for mobile devices! * This property only affects WebGL. * * @static @@ -294,7 +296,8 @@ // TODO: maybe change to SPRITE.BATCH_SIZE: 2000 // TODO: maybe add PARTICLE.BATCH_SIZE: 15000 SPRITE_BATCH_SIZE: 4096, //nice balance between mobile and desktop machines - SPRITE_MAX_TEXTURES: require('./utils/maxRecommendedTextures')(32) //this is the MAXIMUM - various gpus will have there own limits. + SPRITE_MAX_TEXTURES: require('./utils/maxRecommendedTextures')(32), //this is the MAXIMUM - various gpus will have there own limits. + TEXT_STYLE_CHANGED: 'changed' //Name of the event that fires when a text style is changed }; module.exports = CONST; diff --git a/src/core/const.js b/src/core/const.js index a2003c3..470852b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -187,6 +187,8 @@ * If set to DEFAULT, the renderer will occasianally check textures usage. If they are not used for a specified period of time they will be removed from the GPU. * They will of corse be uploaded again when they are required. This is a silent behind the scenes process that should ensure that the GPU does not get filled up. * Handy for mobile devices! + * This property only affects WebGL + * Handy for mobile devices! * This property only affects WebGL. * * @static @@ -294,7 +296,8 @@ // TODO: maybe change to SPRITE.BATCH_SIZE: 2000 // TODO: maybe add PARTICLE.BATCH_SIZE: 15000 SPRITE_BATCH_SIZE: 4096, //nice balance between mobile and desktop machines - SPRITE_MAX_TEXTURES: require('./utils/maxRecommendedTextures')(32) //this is the MAXIMUM - various gpus will have there own limits. + SPRITE_MAX_TEXTURES: require('./utils/maxRecommendedTextures')(32), //this is the MAXIMUM - various gpus will have there own limits. + TEXT_STYLE_CHANGED: 'changed' //Name of the event that fires when a text style is changed }; module.exports = CONST; diff --git a/src/core/index.js b/src/core/index.js index 8b36190..464870a 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -28,7 +28,7 @@ // text Text: require('./text/Text'), - + TextStyle: require('./text/TextStyle'), // primitives Graphics: require('./graphics/Graphics'), GraphicsData: require('./graphics/GraphicsData'), diff --git a/src/core/const.js b/src/core/const.js index a2003c3..470852b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -187,6 +187,8 @@ * If set to DEFAULT, the renderer will occasianally check textures usage. If they are not used for a specified period of time they will be removed from the GPU. * They will of corse be uploaded again when they are required. This is a silent behind the scenes process that should ensure that the GPU does not get filled up. * Handy for mobile devices! + * This property only affects WebGL + * Handy for mobile devices! * This property only affects WebGL. * * @static @@ -294,7 +296,8 @@ // TODO: maybe change to SPRITE.BATCH_SIZE: 2000 // TODO: maybe add PARTICLE.BATCH_SIZE: 15000 SPRITE_BATCH_SIZE: 4096, //nice balance between mobile and desktop machines - SPRITE_MAX_TEXTURES: require('./utils/maxRecommendedTextures')(32) //this is the MAXIMUM - various gpus will have there own limits. + SPRITE_MAX_TEXTURES: require('./utils/maxRecommendedTextures')(32), //this is the MAXIMUM - various gpus will have there own limits. + TEXT_STYLE_CHANGED: 'changed' //Name of the event that fires when a text style is changed }; module.exports = CONST; diff --git a/src/core/index.js b/src/core/index.js index 8b36190..464870a 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -28,7 +28,7 @@ // text Text: require('./text/Text'), - + TextStyle: require('./text/TextStyle'), // primitives Graphics: require('./graphics/Graphics'), GraphicsData: require('./graphics/GraphicsData'), diff --git a/src/core/text/Text.js b/src/core/text/Text.js index ca9d118..d2bb66a 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -1,8 +1,8 @@ var Sprite = require('../sprites/Sprite'), Texture = require('../textures/Texture'), math = require('../math'), - utils = require('../utils'), - CONST = require('../const'); + CONST = require('../const'), + TextStyle = require('./TextStyle'); /** * A Text Object will create a line or multiple lines of text. To split a line you can use '\n' in your text string, @@ -18,29 +18,8 @@ * @extends PIXI.Sprite * @memberof PIXI * @param text {string} The copy that you would like the text to display - * @param [style] {object} The style parameters - * @param [style.font] {string} default 'bold 20px Arial' The style and size of the font - * @param [style.fill='black'] {String|Number} A canvas fillstyle that will be used on the text e.g 'red', '#00FF00' - * @param [style.align='left'] {string} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text - * @param [style.stroke] {String|Number} A canvas fillstyle that will be used on the text stroke e.g 'blue', '#FCFF00' - * @param [style.strokeThickness=0] {number} A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param [style.wordWrap=false] {boolean} Indicates if word wrap should be used - * @param [style.wordWrapWidth=100] {number} The width at which text will wrap, it needs wordWrap to be set to true - * @param [style.letterSpacing=0] {number} The amount of spacing between letters, default is 0 - * @param [style.breakWords=false] {boolean} Indicates if lines can be wrapped within words, it needs wordWrap to be set to true - * @param [style.lineHeight] {number} The line height, a number that represents the vertical space that a letter uses - * @param [style.dropShadow=false] {boolean} Set a drop shadow for the text - * @param [style.dropShadowColor='#000000'] {string} A fill style to be used on the dropshadow e.g 'red', '#00FF00' - * @param [style.dropShadowAngle=Math.PI/4] {number} Set a angle of the drop shadow - * @param [style.dropShadowDistance=5] {number} Set a distance of the drop shadow - * @param [style.dropShadowBlur=0] {number} Set a shadow blur radius - * @param [style.padding=0] {number} Occasionally some fonts are cropped on top or bottom. Adding some padding will - * prevent this from happening by adding padding to the top and bottom of text height. - * @param [style.textBaseline='alphabetic'] {string} The baseline of the text that is rendered. - * @param [style.lineJoin='miter'] {string} The lineJoin property sets the type of corner created, it can resolve - * spiked text issues. Default is 'miter' (creates a sharp corner). - * @param [style.miterLimit=10] {number} The miter limit to use when using the 'miter' lineJoin mode. This can reduce - * or increase the spikiness of rendered text. + * @param [style] {object|TextStyle} The style parameters + * @param [resolution=CONST.RESOLUTION] The resolution of the canvas */ function Text(text, style, resolution) { @@ -78,6 +57,13 @@ * @private */ this._style = null; + /** + * Private listener to track style changes. + * + * @member {Function} + * @private + */ + this._styleListener = null; var texture = Texture.fromCanvas(this.canvas); texture.trim = new math.Rectangle(); @@ -106,10 +92,7 @@ width: { get: function () { - if (this.dirty) - { - this.updateText(); - } + this.updateText(true); return this.scale.x * this._texture._frame.width; }, @@ -129,10 +112,7 @@ height: { get: function () { - if (this.dirty) - { - this.updateText(); - } + this.updateText(true); return this.scale.y * this._texture._frame.height; }, @@ -144,29 +124,9 @@ }, /** - * Set the style of the text + * Set the style of the text. Set up an event listener to listen for changes on the style object and mark the text as dirty. * - * @param [style] {object} The style parameters - * @param [style.font='bold 20pt Arial'] {string} The style and size of the font - * @param [style.fill='black'] {string|number} A canvas fillstyle that will be used on the text eg 'red', '#00FF00' - * @param [style.align='left'] {string} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text - * @param [style.stroke='black'] {string|number} A canvas fillstyle that will be used on the text stroke eg 'blue', '#FCFF00' - * @param [style.strokeThickness=0] {number} A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param [style.wordWrap=false] {boolean} Indicates if word wrap should be used - * @param [style.wordWrapWidth=100] {number} The width at which text will wrap - * @param [style.lineHeight] {number} The line height, a number that represents the vertical space that a letter uses - * @param [style.dropShadow=false] {boolean} Set a drop shadow for the text - * @param [style.dropShadowColor='#000000'] {string|number} A fill style to be used on the dropshadow e.g 'red', '#00FF00' - * @param [style.dropShadowAngle=Math.PI/6] {number} Set a angle of the drop shadow - * @param [style.dropShadowDistance=5] {number} Set a distance of the drop shadow - * @param [style.dropShadowBlur=0] {number} Set a shadow blur radius - * @param [style.padding=0] {number} Occasionally some fonts are cropped on top or bottom. Adding some padding will - * prevent this from happening by adding padding to the top and bottom of text height. - * @param [style.textBaseline='alphabetic'] {string} The baseline of the text that is rendered. - * @param [style.lineJoin='miter'] {string} The lineJoin property sets the type of corner created, it can resolve - * spiked text issues. Default is 'miter' (creates a sharp corner). - * @param [style.miterLimit=10] {number} The miter limit to use when using the 'miter' lineJoin mode. This can reduce - * or increase the spikiness of rendered text. + * @param [style] {object|TextStyle} The style parameters * @memberof PIXI.Text# */ style: { @@ -176,44 +136,19 @@ }, set: function (style) { + if (this._style) { + this._style.off(CONST.TEXT_STYLE_CHANGED, this._onStyleChange, this); + } + style = style || {}; - - if (typeof style.fill === 'number') { - style.fill = utils.hex2string(style.fill); + if (style instanceof TextStyle) + { + this._style = style; + } else + { + this._style = new TextStyle(style); } - - if (typeof style.stroke === 'number') { - style.stroke = utils.hex2string(style.stroke); - } - - if (typeof style.dropShadowColor === 'number') { - style.dropShadowColor = utils.hex2string(style.dropShadowColor); - } - - style.font = style.font || 'bold 20pt Arial'; - style.fill = style.fill || 'black'; - style.align = style.align || 'left'; - style.stroke = style.stroke || 'black'; //provide a default, see: https://github.com/pixijs/pixi.js/issues/136 - style.strokeThickness = style.strokeThickness || 0; - style.wordWrap = style.wordWrap || false; - style.wordWrapWidth = style.wordWrapWidth || 100; - style.breakWords = style.breakWords || false; - style.letterSpacing = style.letterSpacing || 0; - - style.dropShadow = style.dropShadow || false; - style.dropShadowColor = style.dropShadowColor || '#000000'; - style.dropShadowAngle = style.dropShadowAngle !== undefined ? style.dropShadowAngle : Math.PI / 6; - style.dropShadowDistance = style.dropShadowDistance !== undefined ? style.dropShadowDistance : 5; - style.dropShadowBlur = style.dropShadowBlur !== undefined ? style.dropShadowBlur : 0; //shadowBlur is '0' by default according to HTML - - style.padding = style.padding || 0; - - style.textBaseline = style.textBaseline || 'alphabetic'; - - style.lineJoin = style.lineJoin || 'miter'; - style.miterLimit = style.miterLimit || 10; - - this._style = style; + this._style.on(CONST.TEXT_STYLE_CHANGED, this._onStyleChange, this); this.dirty = true; } }, @@ -233,7 +168,7 @@ text = text || ' '; text = text.toString(); - + if (this._text === text) { return; @@ -246,11 +181,14 @@ /** * Renders text and updates it when needed - * + * @param respectDirty {boolean} Whether to abort updating the text if the Text isn't dirty and the function is called. * @private */ -Text.prototype.updateText = function () +Text.prototype.updateText = function (respectDirty) { + if (!this.dirty && respectDirty) { + return; + } var style = this._style; this.context.font = style.font; @@ -461,12 +399,7 @@ */ Text.prototype.renderWebGL = function (renderer) { - if (this.dirty) - { - //this.resolution = 1//renderer.resolution; - - this.updateText(); - } + this.updateText(true); Sprite.prototype.renderWebGL.call(this, renderer); }; @@ -479,12 +412,7 @@ */ Text.prototype._renderCanvas = function (renderer) { - if (this.dirty) - { - // this.resolution = 1//renderer.resolution; - - this.updateText(); - } + this.updateText(true); Sprite.prototype._renderCanvas.call(this, renderer); }; @@ -673,15 +601,20 @@ */ Text.prototype.getBounds = function (matrix) { - if (this.dirty) - { - this.updateText(); - } + this.updateText(true); return Sprite.prototype.getBounds.call(this, matrix); }; /** + * Method to be called upon a TextStyle change. + */ +Text.prototype._onStyleChange = function () +{ + this.dirty = true; +}; + +/** * Destroys this text object. * * @param [destroyBaseTexture=true] {boolean} whether to destroy the base texture as well @@ -692,7 +625,9 @@ this.context = null; this.canvas = null; + this._style.off(CONST.TEXT_STYLE_CHANGED, this._onStyleChange, this); this._style = null; this._texture.destroy(destroyBaseTexture === undefined ? true : destroyBaseTexture); + }; diff --git a/src/core/const.js b/src/core/const.js index a2003c3..470852b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -187,6 +187,8 @@ * If set to DEFAULT, the renderer will occasianally check textures usage. If they are not used for a specified period of time they will be removed from the GPU. * They will of corse be uploaded again when they are required. This is a silent behind the scenes process that should ensure that the GPU does not get filled up. * Handy for mobile devices! + * This property only affects WebGL + * Handy for mobile devices! * This property only affects WebGL. * * @static @@ -294,7 +296,8 @@ // TODO: maybe change to SPRITE.BATCH_SIZE: 2000 // TODO: maybe add PARTICLE.BATCH_SIZE: 15000 SPRITE_BATCH_SIZE: 4096, //nice balance between mobile and desktop machines - SPRITE_MAX_TEXTURES: require('./utils/maxRecommendedTextures')(32) //this is the MAXIMUM - various gpus will have there own limits. + SPRITE_MAX_TEXTURES: require('./utils/maxRecommendedTextures')(32), //this is the MAXIMUM - various gpus will have there own limits. + TEXT_STYLE_CHANGED: 'changed' //Name of the event that fires when a text style is changed }; module.exports = CONST; diff --git a/src/core/index.js b/src/core/index.js index 8b36190..464870a 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -28,7 +28,7 @@ // text Text: require('./text/Text'), - + TextStyle: require('./text/TextStyle'), // primitives Graphics: require('./graphics/Graphics'), GraphicsData: require('./graphics/GraphicsData'), diff --git a/src/core/text/Text.js b/src/core/text/Text.js index ca9d118..d2bb66a 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -1,8 +1,8 @@ var Sprite = require('../sprites/Sprite'), Texture = require('../textures/Texture'), math = require('../math'), - utils = require('../utils'), - CONST = require('../const'); + CONST = require('../const'), + TextStyle = require('./TextStyle'); /** * A Text Object will create a line or multiple lines of text. To split a line you can use '\n' in your text string, @@ -18,29 +18,8 @@ * @extends PIXI.Sprite * @memberof PIXI * @param text {string} The copy that you would like the text to display - * @param [style] {object} The style parameters - * @param [style.font] {string} default 'bold 20px Arial' The style and size of the font - * @param [style.fill='black'] {String|Number} A canvas fillstyle that will be used on the text e.g 'red', '#00FF00' - * @param [style.align='left'] {string} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text - * @param [style.stroke] {String|Number} A canvas fillstyle that will be used on the text stroke e.g 'blue', '#FCFF00' - * @param [style.strokeThickness=0] {number} A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param [style.wordWrap=false] {boolean} Indicates if word wrap should be used - * @param [style.wordWrapWidth=100] {number} The width at which text will wrap, it needs wordWrap to be set to true - * @param [style.letterSpacing=0] {number} The amount of spacing between letters, default is 0 - * @param [style.breakWords=false] {boolean} Indicates if lines can be wrapped within words, it needs wordWrap to be set to true - * @param [style.lineHeight] {number} The line height, a number that represents the vertical space that a letter uses - * @param [style.dropShadow=false] {boolean} Set a drop shadow for the text - * @param [style.dropShadowColor='#000000'] {string} A fill style to be used on the dropshadow e.g 'red', '#00FF00' - * @param [style.dropShadowAngle=Math.PI/4] {number} Set a angle of the drop shadow - * @param [style.dropShadowDistance=5] {number} Set a distance of the drop shadow - * @param [style.dropShadowBlur=0] {number} Set a shadow blur radius - * @param [style.padding=0] {number} Occasionally some fonts are cropped on top or bottom. Adding some padding will - * prevent this from happening by adding padding to the top and bottom of text height. - * @param [style.textBaseline='alphabetic'] {string} The baseline of the text that is rendered. - * @param [style.lineJoin='miter'] {string} The lineJoin property sets the type of corner created, it can resolve - * spiked text issues. Default is 'miter' (creates a sharp corner). - * @param [style.miterLimit=10] {number} The miter limit to use when using the 'miter' lineJoin mode. This can reduce - * or increase the spikiness of rendered text. + * @param [style] {object|TextStyle} The style parameters + * @param [resolution=CONST.RESOLUTION] The resolution of the canvas */ function Text(text, style, resolution) { @@ -78,6 +57,13 @@ * @private */ this._style = null; + /** + * Private listener to track style changes. + * + * @member {Function} + * @private + */ + this._styleListener = null; var texture = Texture.fromCanvas(this.canvas); texture.trim = new math.Rectangle(); @@ -106,10 +92,7 @@ width: { get: function () { - if (this.dirty) - { - this.updateText(); - } + this.updateText(true); return this.scale.x * this._texture._frame.width; }, @@ -129,10 +112,7 @@ height: { get: function () { - if (this.dirty) - { - this.updateText(); - } + this.updateText(true); return this.scale.y * this._texture._frame.height; }, @@ -144,29 +124,9 @@ }, /** - * Set the style of the text + * Set the style of the text. Set up an event listener to listen for changes on the style object and mark the text as dirty. * - * @param [style] {object} The style parameters - * @param [style.font='bold 20pt Arial'] {string} The style and size of the font - * @param [style.fill='black'] {string|number} A canvas fillstyle that will be used on the text eg 'red', '#00FF00' - * @param [style.align='left'] {string} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text - * @param [style.stroke='black'] {string|number} A canvas fillstyle that will be used on the text stroke eg 'blue', '#FCFF00' - * @param [style.strokeThickness=0] {number} A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param [style.wordWrap=false] {boolean} Indicates if word wrap should be used - * @param [style.wordWrapWidth=100] {number} The width at which text will wrap - * @param [style.lineHeight] {number} The line height, a number that represents the vertical space that a letter uses - * @param [style.dropShadow=false] {boolean} Set a drop shadow for the text - * @param [style.dropShadowColor='#000000'] {string|number} A fill style to be used on the dropshadow e.g 'red', '#00FF00' - * @param [style.dropShadowAngle=Math.PI/6] {number} Set a angle of the drop shadow - * @param [style.dropShadowDistance=5] {number} Set a distance of the drop shadow - * @param [style.dropShadowBlur=0] {number} Set a shadow blur radius - * @param [style.padding=0] {number} Occasionally some fonts are cropped on top or bottom. Adding some padding will - * prevent this from happening by adding padding to the top and bottom of text height. - * @param [style.textBaseline='alphabetic'] {string} The baseline of the text that is rendered. - * @param [style.lineJoin='miter'] {string} The lineJoin property sets the type of corner created, it can resolve - * spiked text issues. Default is 'miter' (creates a sharp corner). - * @param [style.miterLimit=10] {number} The miter limit to use when using the 'miter' lineJoin mode. This can reduce - * or increase the spikiness of rendered text. + * @param [style] {object|TextStyle} The style parameters * @memberof PIXI.Text# */ style: { @@ -176,44 +136,19 @@ }, set: function (style) { + if (this._style) { + this._style.off(CONST.TEXT_STYLE_CHANGED, this._onStyleChange, this); + } + style = style || {}; - - if (typeof style.fill === 'number') { - style.fill = utils.hex2string(style.fill); + if (style instanceof TextStyle) + { + this._style = style; + } else + { + this._style = new TextStyle(style); } - - if (typeof style.stroke === 'number') { - style.stroke = utils.hex2string(style.stroke); - } - - if (typeof style.dropShadowColor === 'number') { - style.dropShadowColor = utils.hex2string(style.dropShadowColor); - } - - style.font = style.font || 'bold 20pt Arial'; - style.fill = style.fill || 'black'; - style.align = style.align || 'left'; - style.stroke = style.stroke || 'black'; //provide a default, see: https://github.com/pixijs/pixi.js/issues/136 - style.strokeThickness = style.strokeThickness || 0; - style.wordWrap = style.wordWrap || false; - style.wordWrapWidth = style.wordWrapWidth || 100; - style.breakWords = style.breakWords || false; - style.letterSpacing = style.letterSpacing || 0; - - style.dropShadow = style.dropShadow || false; - style.dropShadowColor = style.dropShadowColor || '#000000'; - style.dropShadowAngle = style.dropShadowAngle !== undefined ? style.dropShadowAngle : Math.PI / 6; - style.dropShadowDistance = style.dropShadowDistance !== undefined ? style.dropShadowDistance : 5; - style.dropShadowBlur = style.dropShadowBlur !== undefined ? style.dropShadowBlur : 0; //shadowBlur is '0' by default according to HTML - - style.padding = style.padding || 0; - - style.textBaseline = style.textBaseline || 'alphabetic'; - - style.lineJoin = style.lineJoin || 'miter'; - style.miterLimit = style.miterLimit || 10; - - this._style = style; + this._style.on(CONST.TEXT_STYLE_CHANGED, this._onStyleChange, this); this.dirty = true; } }, @@ -233,7 +168,7 @@ text = text || ' '; text = text.toString(); - + if (this._text === text) { return; @@ -246,11 +181,14 @@ /** * Renders text and updates it when needed - * + * @param respectDirty {boolean} Whether to abort updating the text if the Text isn't dirty and the function is called. * @private */ -Text.prototype.updateText = function () +Text.prototype.updateText = function (respectDirty) { + if (!this.dirty && respectDirty) { + return; + } var style = this._style; this.context.font = style.font; @@ -461,12 +399,7 @@ */ Text.prototype.renderWebGL = function (renderer) { - if (this.dirty) - { - //this.resolution = 1//renderer.resolution; - - this.updateText(); - } + this.updateText(true); Sprite.prototype.renderWebGL.call(this, renderer); }; @@ -479,12 +412,7 @@ */ Text.prototype._renderCanvas = function (renderer) { - if (this.dirty) - { - // this.resolution = 1//renderer.resolution; - - this.updateText(); - } + this.updateText(true); Sprite.prototype._renderCanvas.call(this, renderer); }; @@ -673,15 +601,20 @@ */ Text.prototype.getBounds = function (matrix) { - if (this.dirty) - { - this.updateText(); - } + this.updateText(true); return Sprite.prototype.getBounds.call(this, matrix); }; /** + * Method to be called upon a TextStyle change. + */ +Text.prototype._onStyleChange = function () +{ + this.dirty = true; +}; + +/** * Destroys this text object. * * @param [destroyBaseTexture=true] {boolean} whether to destroy the base texture as well @@ -692,7 +625,9 @@ this.context = null; this.canvas = null; + this._style.off(CONST.TEXT_STYLE_CHANGED, this._onStyleChange, this); this._style = null; this._texture.destroy(destroyBaseTexture === undefined ? true : destroyBaseTexture); + }; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js new file mode 100644 index 0000000..76951ba --- /dev/null +++ b/src/core/text/TextStyle.js @@ -0,0 +1,334 @@ +var EventEmitter = require('eventemitter3'), + CONST = require('../const'), + utils = require('../utils'); + +/** + * A TextStyle Object decorates a Text Object. It acts as an event emitter, and can be shared between + * multiple Text objects. + * + * @class + * @extends EventEmitter + * @memberof PIXI + * @param [style] {object} The style parameters + * @param [style.font='bold 20pt Arial'] {string} The style and size of the font + * @param [style.fill='black'] {string|number} A canvas fillstyle that will be used on the text e.g 'red', '#00FF00' + * @param [style.align='left'] {string} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text + * @param [style.stroke='black'] {string|number} A canvas fillstyle that will be used on the text stroke e.g 'blue', '#FCFF00' + * @param [style.strokeThickness=0] {number} A number that represents the thickness of the stroke. Default is 0 (no stroke) + * @param [style.wordWrap=false] {boolean} Indicates if word wrap should be used + * @param [style.wordWrapWidth=100] {number} The width at which text will wrap, it needs wordWrap to be set to true + * @param [style.lineHeight] {number} The line height, a number that represents the vertical space that a letter uses + * @param [style.dropShadow=false] {boolean} Set a drop shadow for the text + * @param [style.dropShadowColor='#000000'] {string} A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param [style.dropShadowAngle=Math.PI/4] {number} Set a angle of the drop shadow + * @param [style.dropShadowDistance=5] {number} Set a distance of the drop shadow + * @param [style.dropShadowBlur=0] {number} Set a shadow blur radius + * @param [style.padding=0] {number} Occasionally some fonts are cropped on top or bottom. Adding some padding will + * prevent this from happening by adding padding to the top and bottom of text height. + * @param [style.textBaseline='alphabetic'] {string} The baseline of the text that is rendered. + * @param [style.lineJoin='miter'] {string} The lineJoin property sets the type of corner created, it can resolve + * spiked text issues. Default is 'miter' (creates a sharp corner). + * @param [style.miterLimit=10] {number} The miter limit to use when using the 'miter' lineJoin mode. This can reduce + * or increase the spikiness of rendered text. + */ +function TextStyle(style) +{ + EventEmitter.call(this); + Object.assign(this, this._defaults, style); +} + +TextStyle.prototype = Object.create(EventEmitter.prototype); +TextStyle.prototype.constructor = TextStyle; +module.exports = TextStyle; + +// Default settings. Explained in the constructor. +TextStyle.prototype._defaults = { + align: 'left', + dropShadow: false, + dropShadowAngle: Math.PI / 6, + dropShadowBlur: 0, + dropShadowColor: '#000000', + dropShadowDistance: 5, + lineHeight: null, + lineJoin: 'miter', + fill: 'black', + font: 'bold 20pt Arial', + miterLimit: 10, + padding: 0, + stroke: 'black', + strokeThickness: 0, + textBaseline: 'alphabetic', + wordWrap: false, + wordWrapWidth: 100 +}; + +/** + * Creates a new TextStyle object with the same values as this one. + * Note that the only the properties of the object are cloned, not its event emitter. + * + * @return {PIXI.TextStyle} + */ +TextStyle.prototype.clone = function () +{ + var clonedProperties = {}; + for (var key in this._defaults) + { + clonedProperties[key] = this[key]; + } + return new TextStyle(clonedProperties); +}; + +/** + * Resets all properties to the defaults specified in TextStyle.prototype._default + */ +TextStyle.prototype.reset = function () +{ + Object.assign(this, this._defaults); +}; + +/** + * Create setters and getters for each of the style properties. Converts colors where necessary. + * Any set operation will emit a styleChanged event. + */ +Object.defineProperties(TextStyle.prototype, { + font: { + get: function () + { + return this._font; + }, set: function (font) + { + if (this._font !== font) + { + this._font = font; + this.emit(CONST.TEXT_STYLE_CHANGED); + } + } + }, + fill: { + get: function () + { + return this._fill; + }, set: function (fill) + { + var outputColor = getColor(fill); + if (this._fill !== outputColor) + { + this._fill = outputColor; + this.emit(CONST.TEXT_STYLE_CHANGED); + } + } + }, + align: { + get: function () + { + return this._align; + }, set: function (align) + { + if (this._align !== align) + { + this._align = align; + this.emit(CONST.TEXT_STYLE_CHANGED); + } + } + }, + stroke: { + get: function () + { + return this._stroke; + }, set: function (stroke) + { + var outputColor = getColor(stroke); + if (this._stroke !== outputColor) + { + this._stroke = outputColor; + this.emit(CONST.TEXT_STYLE_CHANGED); + } + } + }, + strokeThickness: { + get: function () + { + return this._strokeThickness; + }, set: function (strokeThickness) + { + if (this._strokeThickness !== strokeThickness) + { + this._strokeThickness = strokeThickness; + this.emit(CONST.TEXT_STYLE_CHANGED); + } + } + }, + wordWrap: { + get: function () + { + return this._wordWrap; + }, set: function (wordWrap) + { + if (this._wordWrap !== wordWrap) + { + this._wordWrap = wordWrap; + this.emit(CONST.TEXT_STYLE_CHANGED); + } + } + }, + wordWrapWidth: { + get: function () + { + return this._wordWrapWidth; + }, set: function (wordWrapWidth) + { + if (this._wordWrapWidth !== wordWrapWidth) + { + this._wordWrapWidth = wordWrapWidth; + this.emit(CONST.TEXT_STYLE_CHANGED); + } + } + }, + dropShadow: { + get: function () + { + return this._dropShadow; + }, set: function (dropShadow) + { + if (this._dropShadow !== dropShadow) + { + this._dropShadow = dropShadow; + this.emit(CONST.TEXT_STYLE_CHANGED); + } + } + }, + dropShadowColor: { + get: function () + { + return this._dropShadowColor; + }, set: function (dropShadowColor) + { + var outputColor = getColor(dropShadowColor); + if (this._dropShadowColor !== outputColor) + { + this._dropShadowColor = outputColor; + this.emit(CONST.TEXT_STYLE_CHANGED); + } + } + }, + dropShadowAngle: { + get: function () + { + return this._dropShadowAngle; + }, set: function (dropShadowAngle) + { + if (this._dropShadowAngle !== dropShadowAngle) + { + this._dropShadowAngle = dropShadowAngle; + this.emit(CONST.TEXT_STYLE_CHANGED); + } + } + }, + dropShadowDistance: { + get: function () + { + return this._dropShadowDistance; + }, set: function (dropShadowDistance) + { + if (this._dropShadowDistance !== dropShadowDistance) + { + this._dropShadowDistance = dropShadowDistance; + this.emit(CONST.TEXT_STYLE_CHANGED); + } + } + }, + dropShadowBlur: { + get: function () + { + return this._dropShadowBlur; + }, set: function (dropShadowBlur) + { + if (this._dropShadowBlur !== dropShadowBlur) + { + this._dropShadowBlur = dropShadowBlur; + this.emit(CONST.TEXT_STYLE_CHANGED); + } + } + }, + padding: { + get: function () + { + return this._padding; + }, set: function (padding) + { + if (this._padding !== padding) + { + this._padding = padding; + this.emit(CONST.TEXT_STYLE_CHANGED); + } + } + }, + textBaseline: { + get: function () + { + return this._textBaseline; + }, set: function (textBaseline) + { + if (this._textBaseline !== textBaseline) + { + this._textBaseline = textBaseline; + this.emit(CONST.TEXT_STYLE_CHANGED); + } + } + }, + lineJoin: { + get: function () + { + return this._lineJoin; + }, set: function (lineJoin) + { + if (this._lineJoin !== lineJoin) + { + this._lineJoin = lineJoin; + this.emit(CONST.TEXT_STYLE_CHANGED); + } + } + }, + lineHeight: { + get: function () + { + return this._lineHeight; + }, set: function (lineHeight) + { + if (this._lineHeight !== lineHeight) + { + this._lineHeight = lineHeight; + this.emit(CONST.TEXT_STYLE_CHANGED); + } + } + }, + miterLimit: { + get: function () + { + return this._miterLimit; + }, set: function (miterLimit) + { + if (this._miterLimit !== miterLimit) + { + this._miterLimit = miterLimit; + this.emit(CONST.TEXT_STYLE_CHANGED); + } + } + } +}); + +/** + * Utility function to convert hexadecimal colors to strings, and simply return the color if it's a string. + * + * @return {string} The color as a string. + */ +function getColor(color) +{ + if (typeof color === 'number') + { + return utils.hex2string(color); + } else + { + return color; + } +}