scaffold.libs_as.starling.text.TextField.as Maven / Gradle / Ivy
// =================================================================================================
//
// Starling Framework
// Copyright 2011-2015 Gamua. All Rights Reserved.
//
// This program is free software. You can redistribute and/or modify it
// in accordance with the terms of the accompanying license agreement.
//
// =================================================================================================
package starling.text
{
import flash.display3D.Context3DTextureFormat;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.utils.Dictionary;
import starling.core.Starling;
import starling.display.DisplayObject;
import starling.display.DisplayObjectContainer;
import starling.display.MeshBatch;
import starling.display.Quad;
import starling.display.Sprite;
import starling.events.Event;
import starling.rendering.Painter;
import starling.utils.RectangleUtil;
/** A TextField displays text, either using standard true type fonts or custom bitmap fonts.
*
* You can set all properties you are used to, like the font name and size, a color, the
* horizontal and vertical alignment, etc. The border property is helpful during development,
* because it lets you see the bounds of the TextField.
*
* There are two types of fonts that can be displayed:
*
*
* - Standard TrueType fonts. This renders the text just like a conventional Flash
* TextField. It is recommended to embed the font, since you cannot be sure which fonts
* are available on the client system, and since this enhances rendering quality.
* Simply pass the font name to the corresponding property.
* - Bitmap fonts. If you need speed or fancy font effects, use a bitmap font instead.
* That is a font that has its glyphs rendered to a texture atlas. To use it, first
* register the font with the method
registerBitmapFont
, and then pass
* the font name to the corresponding property of the text field.
*
*
* For bitmap fonts, we recommend one of the following tools:
*
*
* - Windows: Bitmap Font Generator
* from Angel Code (free). Export the font data as an XML file and the texture as a png
* with white characters on a transparent background (32 bit).
* - Mac OS: Glyph Designer from
* 71squared or bmGlyph (both commercial).
* They support Starling natively.
*
*
* When using a bitmap font, the 'color' property is used to tint the font texture. This
* works by multiplying the RGB values of that property with those of the texture's pixel.
* If your font contains just a single color, export it in plain white and change the 'color'
* property to any value you like (it defaults to zero, which means black). If your font
* contains multiple colors, change the 'color' property to Color.WHITE
to get
* the intended result.
*
* Batching of TextFields
*
* Normally, TextFields will require exactly one draw call. For TrueType fonts, you cannot
* avoid that; bitmap fonts, however, may be batched as long as the batchable
* property is enabled. Since only batchable meshes can make use of Starling's render cache,
* it makes sense to keep that property enabled in most circumstances. Only TextFields that
* are constantly changing (their position relative to the stage, their color, text, etc.)
* and contain are large number of bitmap glyphs should disable batching.
*/
public class TextField extends DisplayObjectContainer
{
// the name container with the registered bitmap fonts
private static const BITMAP_FONT_DATA_NAME:String = "starling.display.TextField.BitmapFonts";
private var _text:String;
private var _options:TextOptions;
private var _format:TextFormat;
private var _autoSize:String;
private var _textBounds:Rectangle;
private var _hitArea:Rectangle;
private var _compositor:ITextCompositor;
private var _requiresRecomposition:Boolean;
private var _border:DisplayObjectContainer;
private var _meshBatch:MeshBatch;
// helper objects
private static var sMatrix:Matrix = new Matrix();
private static var sTrueTypeCompositor:TrueTypeCompositor = new TrueTypeCompositor();
private static var sDefaultTextureFormat:String = Context3DTextureFormat.BGRA_PACKED;
private var _helperFormat:TextFormat = new TextFormat();
/** Create a new text field with the given properties. */
public function TextField(width:int, height:int, text:String="", format:TextFormat=null)
{
_text = text ? text : "";
_autoSize = TextFieldAutoSize.NONE;
_hitArea = new Rectangle(0, 0, width, height);
_requiresRecomposition = true;
_compositor = sTrueTypeCompositor;
_options = new TextOptions();
_format = format ? format.clone() : new TextFormat();
_format.addEventListener(Event.CHANGE, setRequiresRecomposition);
_meshBatch = new MeshBatch();
_meshBatch.touchable = false;
addChild(_meshBatch);
}
/** Disposes the underlying texture data. */
public override function dispose():void
{
_format.removeEventListener(Event.CHANGE, setRequiresRecomposition);
_compositor.clearMeshBatch(_meshBatch);
super.dispose();
}
/** @inheritDoc */
public override function render(painter:Painter):void
{
if (_requiresRecomposition) recompose();
super.render(painter);
}
/** Forces the text contents to be composed right away.
* Normally, it will only do so lazily, i.e. before being rendered. */
private function recompose():void
{
if (_requiresRecomposition)
{
_compositor.clearMeshBatch(_meshBatch);
var font:String = _format.font;
var bitmapFont:BitmapFont = getBitmapFont(font);
if (bitmapFont == null && font == BitmapFont.MINI)
{
bitmapFont = new BitmapFont();
registerBitmapFont(bitmapFont);
}
_compositor = bitmapFont ? bitmapFont : sTrueTypeCompositor;
updateText();
updateBorder();
_requiresRecomposition = false;
}
}
// font and border rendering
private function updateText():void
{
var width:Number = _hitArea.width;
var height:Number = _hitArea.height;
var format:TextFormat = _helperFormat;
// By working on a copy of the TextFormat, we make sure that modifications done
// within the 'fillMeshBatch' method do not cause any side effects.
//
// (We cannot use a static variable, because that might lead to problems when
// recreating textures after a context loss.)
format.copyFrom(_format);
if (isHorizontalAutoSize) width = 100000;
if (isVerticalAutoSize) height = 100000;
_options.textureScale = Starling.contentScaleFactor;
_options.textureFormat = sDefaultTextureFormat;
_compositor.fillMeshBatch(_meshBatch, width, height, _text, format, _options);
if (_autoSize != TextFieldAutoSize.NONE)
{
_textBounds = _meshBatch.getBounds(_meshBatch, _textBounds);
if (isHorizontalAutoSize)
{
_meshBatch.x = _textBounds.x = -_textBounds.x;
_hitArea.width = _textBounds.width;
}
if (isVerticalAutoSize)
{
_meshBatch.y = _textBounds.y = -_textBounds.y;
_hitArea.height = _textBounds.height;
}
}
else
{
// hit area doesn't change, and text bounds can be created on demand
_textBounds = null;
}
}
private function updateBorder():void
{
if (_border == null) return;
var width:Number = _hitArea.width;
var height:Number = _hitArea.height;
var topLine:Quad = _border.getChildAt(0) as Quad;
var rightLine:Quad = _border.getChildAt(1) as Quad;
var bottomLine:Quad = _border.getChildAt(2) as Quad;
var leftLine:Quad = _border.getChildAt(3) as Quad;
topLine.width = width; topLine.height = 1;
bottomLine.width = width; bottomLine.height = 1;
leftLine.width = 1; leftLine.height = height;
rightLine.width = 1; rightLine.height = height;
rightLine.x = width - 1;
bottomLine.y = height - 1;
topLine.color = rightLine.color = bottomLine.color = leftLine.color = _format.color;
}
private function setRequiresRecomposition():void
{
_requiresRecomposition = true;
setRequiresRedraw();
}
// properties
private function get isHorizontalAutoSize():Boolean
{
return _autoSize == TextFieldAutoSize.HORIZONTAL ||
_autoSize == TextFieldAutoSize.BOTH_DIRECTIONS;
}
private function get isVerticalAutoSize():Boolean
{
return _autoSize == TextFieldAutoSize.VERTICAL ||
_autoSize == TextFieldAutoSize.BOTH_DIRECTIONS;
}
/** Returns the bounds of the text within the text field. */
public function get textBounds():Rectangle
{
if (_requiresRecomposition) recompose();
if (_textBounds == null) _textBounds = _meshBatch.getBounds(_meshBatch);
return _textBounds.clone();
}
/** @inheritDoc */
public override function getBounds(targetSpace:DisplayObject, out:Rectangle=null):Rectangle
{
if (_requiresRecomposition) recompose();
getTransformationMatrix(targetSpace, sMatrix);
return RectangleUtil.getBounds(_hitArea, sMatrix, out);
}
/** @inheritDoc */
public override function hitTest(localPoint:Point):DisplayObject
{
if (!visible || !touchable || !hitTestMask(localPoint)) return null;
else if (_hitArea.containsPoint(localPoint)) return this;
else return null;
}
/** @inheritDoc */
public override function set width(value:Number):void
{
// different to ordinary display objects, changing the size of the text field should
// not change the scaling, but make the texture bigger/smaller, while the size
// of the text/font stays the same (this applies to the height, as well).
_hitArea.width = value;
setRequiresRecomposition();
}
/** @inheritDoc */
public override function set height(value:Number):void
{
_hitArea.height = value;
setRequiresRecomposition();
}
/** The displayed text. */
public function get text():String { return _text; }
public function set text(value:String):void
{
if (value == null) value = "";
if (_text != value)
{
_text = value;
setRequiresRecomposition();
}
}
/** The format describes how the text will be rendered, describing the font name and size,
* color, alignment, etc.
*
* Note that you can edit the font properties directly; there's no need to reassign
* the format for the changes to show up.
*
*
* var textField:TextField = new TextField(100, 30, "Hello Starling");
* textField.format.font = "Arial";
* textField.format.color = Color.RED;
*
* @default Verdana, 12 pt, black, centered
*/
public function get format():TextFormat { return _format; }
public function set format(value:TextFormat):void
{
if (value == null) throw new ArgumentError("format cannot be null");
_format.copyFrom(value);
}
/** Draws a border around the edges of the text field. Useful for visual debugging.
* @default false */
public function get border():Boolean { return _border != null; }
public function set border(value:Boolean):void
{
if (value && _border == null)
{
_border = new Sprite();
addChild(_border);
for (var i:int=0; i<4; ++i)
_border.addChild(new Quad(1.0, 1.0));
updateBorder();
}
else if (!value && _border != null)
{
_border.removeFromParent(true);
_border = null;
}
}
/** Indicates whether the font size is automatically reduced if the complete text does
* not fit into the TextField. @default false */
public function get autoScale():Boolean { return _options.autoScale; }
public function set autoScale(value:Boolean):void
{
if (_options.autoScale != value)
{
_options.autoScale = value;
setRequiresRecomposition();
}
}
/** Specifies the type of auto-sizing the TextField will do.
* Note that any auto-sizing will implicitly deactivate all auto-scaling.
* @default none */
public function get autoSize():String { return _autoSize; }
public function set autoSize(value:String):void
{
if (_autoSize != value)
{
_autoSize = value;
setRequiresRecomposition();
}
}
/** Indicates if the text should be wrapped at word boundaries if it does not fit into
* the TextField otherwise. @default true */
public function get wordWrap():Boolean { return _options.wordWrap; }
public function set wordWrap(value:Boolean):void
{
if (value != _options.wordWrap)
{
_options.wordWrap = value;
setRequiresRecomposition();
}
}
/** Indicates if TextField should be batched on rendering.
*
* Only batchable meshes can profit from the render cache; but batching large meshes
* may take up a lot of CPU time. Thus, for large bitmap font text fields (i.e. many
* glyphs) that are constantly changing (i.e. can't use the render cache anyway), it
* makes sense to deactivate batching.
*
* @default true
*/
public function get batchable():Boolean { return _meshBatch.batchable; }
public function set batchable(value:Boolean):void
{
_meshBatch.batchable = value;
}
/** Indicates if text should be interpreted as HTML code. For a description
* of the supported HTML subset, refer to the classic Flash 'TextField' documentation.
* Clickable hyperlinks and external images are not supported. Only works for
* TrueType fonts! @default false */
public function get isHtmlText():Boolean { return _options.isHtmlText; }
public function set isHtmlText(value:Boolean):void
{
if (_options.isHtmlText != value)
{
_options.isHtmlText = value;
setRequiresRecomposition();
}
}
/** The Context3D texture format that is used for rendering of all TrueType texts.
* The default (Context3DTextureFormat.BGRA_PACKED
) provides a good
* compromise between quality and memory consumption; use BGRA
for
* the highest quality. */
public static function get defaultTextureFormat():String { return sDefaultTextureFormat; }
public static function set defaultTextureFormat(value:String):void
{
sDefaultTextureFormat = value;
}
/** Makes a bitmap font available at any TextField in the current stage3D context.
* The font is identified by its name
(not case sensitive).
* Per default, the name
property of the bitmap font will be used, but you
* can pass a custom name, as well. @return the name of the font. */
public static function registerBitmapFont(bitmapFont:BitmapFont, name:String=null):String
{
if (name == null) name = bitmapFont.name;
bitmapFonts[convertToLowerCase(name)] = bitmapFont;
return name;
}
/** Unregisters the bitmap font and, optionally, disposes it. */
public static function unregisterBitmapFont(name:String, dispose:Boolean=true):void
{
name = convertToLowerCase(name);
if (dispose && bitmapFonts[name] != undefined)
bitmapFonts[name].dispose();
delete bitmapFonts[name];
}
/** Returns a registered bitmap font (or null, if the font has not been registered).
* The name is not case sensitive. */
public static function getBitmapFont(name:String):BitmapFont
{
return bitmapFonts[convertToLowerCase(name)];
}
/** Stores the currently available bitmap fonts. Since a bitmap font will only work
* in one Stage3D context, they are saved in Starling's 'contextData' property. */
private static function get bitmapFonts():Dictionary
{
var fonts:Dictionary = Starling.painter.sharedData[BITMAP_FONT_DATA_NAME] as Dictionary;
if (fonts == null)
{
fonts = new Dictionary();
Starling.painter.sharedData[BITMAP_FONT_DATA_NAME] = fonts;
}
return fonts;
}
// optimization for 'toLowerCase' calls
private static var sStringCache:Dictionary = new Dictionary();
private static function convertToLowerCase(string:String):String
{
var result:String = sStringCache[string];
if (result == null)
{
result = string.toLowerCase();
sStringCache[string] = result;
}
return result;
}
}
}