/** * @class Ext.draw.sprite.Text * @extends Ext.draw.sprite.Sprite * * A sprite that represents text. * * @example preview miniphone * var component = new Ext.draw.Component({ * items: [{ * type: 'text', * x: 50, * y: 50, * text: 'Sencha', * fontSize: 18, * fillStyle: 'blue' * }] * }); * Ext.Viewport.setLayout('fit'); * Ext.Viewport.add(component); */ Ext.define("Ext.draw.sprite.Text", { extend: "Ext.draw.sprite.Sprite", requires: ['Ext.draw.TextMeasurer'], alias: 'sprite.text', type: 'text', lineBreakRe: /\n/g, inheritableStatics: { shortHand1Re: /'(.*)'/g, shortHand2Re: / /g, shortHand3Re: /\s*,\s*/g, shortHand4Re: /\$\$\$\$/g, def: { processors: { /** * @cfg {Number} [x=0] The position of the sprite on the x-axis. */ x: "number", /** * @cfg {Number} [y=0] The position of the sprite on the y-axis. */ y: "number", /** * @cfg {String} [text=''] The text represented in the sprite. */ text: "string", /** * @cfg {String/Number} [fontSize='10px'] The size of the font displayed. */ fontSize: function (n) { if (!isNaN(n)) { return +n + 'px'; } else if (n.match(Ext.dom.Element.unitRe)) { return n; } }, /** * @cfg {String} [fontStyle=''] The style of the font displayed. {normal, italic, oblique} */ fontStyle: "enums(,italic,oblique)", /** * @cfg {String} [fontVariant=''] The variant of the font displayed. {normal, small-caps} */ fontVariant: "enums(,small-caps)", /** * @cfg {String} [fontWeight=''] The weight of the font displayed. {normal, bold, bolder, lighter} */ fontWeight: (function (fontWeights) { return function (n) { if (!n) { return ""; } else if (n === 'normal') { return ''; } else if (!isNaN(n)) { n = +n; if (100 <= n && n <= 900) { return n; } } else if (n in fontWeights) { return n; } }; })({"normal": true, "bold": true, "bolder": true, "lighter": true}), /** * @cfg {String} [fontFamily='sans-serif'] The family of the font displayed. */ fontFamily: "string", /** * @cfg {String} [textAlign='start'] The alignment of the text displayed. {left, right, center, start, end} */ textAlign: (function (textAligns) { return function (n) { if (n === 'middle') { return 'center'; } else if (!n) { return "center"; } else if (!Ext.isString(n)) { return undefined; } else if (n in textAligns) { return n; } }; })({"left": true, "right": true, "center": true, "start": true, "end": true}), /** * @cfg {String} [textBaseline="alphabetic"] The baseline of the text displayed. {top, hanging, middle, alphabetic, ideographic, bottom} */ textBaseline: (function (textBaselines) { return function (n) { if (n === false) { return "alphabetic"; } else if (n in textBaselines) { return n; } else if (n === 'center') { return 'middle'; } }; })({"top": true, "hanging": true, "middle": true, "alphabetic": true, "ideographic": true, "bottom": true}), /** * @cfg {String} [font='10px sans-serif'] The font displayed. */ font: "string" }, aliases: { "font-size": "fontSize", "font-family": "fontFamily", "font-weight": "fontWeight", "font-variant": "fontVariant", "text-anchor": "textAlign" }, defaults: { fontStyle: '', fontVariant: '', fontWeight: '', fontSize: '10px', fontFamily: 'sans-serif', font: '10px sans-serif', textBaseline: "alphabetic", textAlign: "start", strokeStyle: 'rgba(0, 0, 0, 0)', divBased: true, fillStyle: '#000', x: 0, y: 0, text: '' }, dirtyTriggers: { fontStyle: 'font,bbox', fontVariant: 'font,bbox', fontWeight: 'font,bbox', fontSize: 'font,bbox', fontFamily: 'font,bbox', font: 'font-short-hand,bbox,canvas', textBaseline: 'bbox', textAlign: 'bbox', x: "bbox", y: "bbox", text: "bbox" }, updaters: { "font-short-hand": (function (dispatcher) { return function (attrs) { // TODO: Do this according to http://www.w3.org/TR/CSS21/fonts.html#font-shorthand var value = attrs.font, parts, part, i, ln, dispKey; value = value.replace(Ext.draw.sprite.Text.shortHand1Re, function (a, arg1) { return arg1.replace(Ext.draw.sprite.Text.shortHand2Re, '$$$$'); }); value = value.replace(Ext.draw.sprite.Text.shortHand3Re, ','); parts = value.split(' '); attrs = {}; for (i = 0, ln = parts.length; i < ln; i++) { part = parts[i]; dispKey = dispatcher[part]; if (dispKey) { attrs[dispKey] = part; } else if (part.match(Ext.dom.Element.unitRe)) { attrs.fontSize = part; } else { attrs.fontFamily = part.replace(Ext.draw.sprite.Text.shortHand4Re, ' '); } } this.setAttributes(attrs, true); }; })({ "italic": "fontStyles", "oblique": "fontStyles", "bold": "fontWeights", "bolder": "fontWeights", "lighter": "fontWeights", "100": "fontWeights", "200": "fontWeights", "300": "fontWeights", "400": "fontWeights", "500": "fontWeights", "600": "fontWeights", "700": "fontWeights", "800": "fontWeights", "900": "fontWeights", "small-caps": "fontVariant" }), "font": function (attrs) { var font = ''; if (attrs.fontWeight) { font += attrs.fontWeight + ' '; } if (attrs.fontVariant) { font += attrs.fontVariant + ' '; } if (attrs.fontSize) { font += attrs.fontSize + ' '; } if (attrs.fontFamily) { font += attrs.fontFamily + ' '; } this.setAttributes({ font: font.substr(0, font.length - 1) }, true); } } } }, constructor: function (config) { Ext.draw.sprite.Sprite.prototype.constructor.call(this, config); }, updatePlainBBox: function (plain) { var me = this, attr = me.attr, x = attr.x, y = attr.y, dx = [], font = attr.font, text = attr.text, baseline = attr.textBaseline, alignment = attr.textAlign, size = Ext.draw.TextMeasurer.measureText(text, font), sizes = size.sizes, height = size.height, width = size.width, ln = sizes ? sizes.length : 0, i = 0; switch (baseline) { case 'hanging' : case 'top': break; case 'ideographic' : case 'bottom' : y -= height; break; case 'alphabetic' : y -= height * 0.8; break; case 'middle' : case 'center' : y -= height * 0.5; break; } switch (alignment) { case 'end' : case 'right' : x -= width; for (; i < ln; i++) { dx.push(width - sizes[i].width); } break; case 'middle' : case 'center' : x -= width * 0.5; for (; i < ln; i++) { dx.push((width - sizes[i].width) * 0.5); } break; } attr.textAlignOffsets = dx; plain.x = x; plain.y = y; plain.width = width; plain.height = height; }, setText: function (text) { this.setAttributes({text: text}, true); }, setElementStyles: function (element, styles) { var stylesCache = element.stylesCache || (element.stylesCache = {}), style = element.dom.style, name; for (name in styles) { if (stylesCache[name] !== styles[name]) { stylesCache[name] = style[name] = styles[name]; } } }, render: function (surface, ctx) { var attr = this.attr, mat = Ext.draw.Matrix.fly(attr.matrix.elements.slice(0)), bbox = this.getBBox(true), dx = attr.textAlignOffsets, x, y, i, lines; if (attr.text.length === 0) { return; } lines = attr.text.split('\n'); // Simulate textBaseline and textAlign. x = attr.bbox.plain.x; y = attr.bbox.plain.y; mat.toContext(ctx); for (i = 0; i < lines.length; i++) { if (ctx.fillStyle !== 'rgba(0, 0, 0, 0)') { ctx.fillText(lines[i], x + (dx[i] || 0), y + bbox.height / lines.length * i); } if (ctx.strokeStyle !== 'rgba(0, 0, 0, 0)') { ctx.strokeText(lines[i], x + (dx[i] || 0), y + bbox.height / lines.length * i); } } } });