UNPKG

blossom

Version:

Modern, Cross-Platform Application Framework

174 lines (141 loc) 5.37 kB
// ========================================================================== // Project: Blossom - Modern, Cross-Platform Application Framework // Copyright: ©2012 Fohr Motion Picture Studios. All rights reserved. // License: Licensed under the GPLv3 license (see BLOSSOM-LICENSE). // ========================================================================== /*globals sc_assert formatter linebreak */ sc_require('layers/layer'); sc_require('text/linebreak'); sc_require('text/formatter'); var base3 = "#fdf6e3"; var base03 = "#002b36"; SC.TextLayer = SC.Layer.extend({ isTextLayer: true, displayProperties: 'value'.w(), // FIXME: Add more text properties. font: "11pt Helvetica, sans", color: base03, backgroundColor: base3, textBaseline: 'top', textAlign: 'left', tolerance: 10, lineHeight: 18, isSingleLine: false, _sc_textPropertiesDidChange: function() { this.__needsTextLayout__ = true; var surface = this.get('surface'); if (surface) surface.triggerLayoutAndRendering(); }.observes('font', 'color', 'backgroundColor', 'textBaseline', 'textBaseline', 'tolerance', 'lineHeight'), value: null, // should be a String or null _sc_value: null, _sc_valueDidChange: function() { var value = this.get('value'); if (value !== this._sc_value) { this._sc_value = value; if (value) { this.__needsTextLayout__ = true; var surface = this.get('surface'); if (surface) surface.triggerLayoutAndRendering(); } } }.observes('value'), updateTextLayout: function(context) { // console.log('SC.TextLayer#updateTextLayout()'); var text = String(this.get('value') || ''), line, that = this; this.__needsTextLayout__ = false; sc_assert(context); function setparagraph(nodes, breaks, lineLengths, center) { var i = 0, lines = [], point, j, r, lineStart = 0, maxLength = Math.max.apply(null, lineLengths); // Iterate through the line breaks, and split the nodes at the correct // point. for (i = 1; i < breaks.length; i += 1) { point = breaks[i].position; r = breaks[i].ratio; for (j = lineStart; j < nodes.length; j += 1) { // After a line break, we skip any nodes unless they are boxes or // forced breaks. if (nodes[j].type === 'box' || (nodes[j].type === 'penalty' && nodes[j].penalty === -linebreak.defaults.infinity)) { lineStart = j; break; } } lines.push({ ratio: r, nodes: nodes.slice(lineStart, point + 1), position: point }); lineStart = point; } return lines; } function align(type, lineLengths, tolerance, center) { var format, nodes, breaks, lines, height; context.textBaseline = that.get('textBaseline'); context.font = that.get('font'); format = formatter(function(str) { return context.measureText(str).width; }); nodes = format[type](text); breaks = linebreak(nodes, lineLengths, { tolerance: tolerance }); if (!breaks.isEmpty()) { lines = that._sc_lines = setparagraph(nodes, breaks, lineLengths, center); // Subtle, we don't want to trigger layout, which would reposition // our layer. if (that.get('isSingleLine')) { height = Math.max(that.get('layout').minHeight || 0, that.get('lineHeight')); } else { height = Math.max(that.get('layout').minHeight || 0, lines.length*that.get('lineHeight')); } that._sc_bounds[3]/*height*/ = height; that.triggerRendering(); } else { console.log('Paragraph can not be set with the given tolerance.', tolerance); that._sc_lines = []; } } align(this.get('textAlign'), [this.get('bounds')[2]/*width*/], this.get('tolerance')); }, render: function(context) { // console.log('SC.TextLayer#render()'); var lines = this._sc_lines, lineLengths = [this.get('bounds')[2]/*width*/], maxLength = Math.max.apply(null, lineLengths), lineHeight = this.get('lineHeight'), center = false, y = 0, bounds = this.get('bounds'), backgroundColor = this.get('backgroundColor'); sc_assert(!this.__needsTextLayout__); sc_assert(lines); if (backgroundColor) { context.fillStyle = backgroundColor; context.fillRect(0, 0, bounds.width, bounds.height); } context.textBaseline = this.get('textBaseline'); context.font = this.get('font'); context.fillStyle = this.get('color'); lines.forEach(function (line, lineIndex) { var x = 0, lineLength = lineIndex < lineLengths.length ? lineLengths[lineIndex] : lineLengths[lineLengths.length - 1]; if (center) { x += (maxLength - lineLength) / 2; } line.nodes.forEach(function (node, index) { if (node.type === 'box') { context.fillText(node.value, x, y); x += node.width; } else if (node.type === 'glue') { x += node.width + line.ratio * (line.ratio < 0 ? node.shrink : node.stretch); } }); y += lineHeight; }); }, init: function() { arguments.callee.base.apply(this, arguments); this._sc_valueDidChange(); } });