//////////////////////////////////////////////////////////////////////////////// // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package spark.components { import flash.display.DisplayObject; import flash.display.DisplayObjectContainer; import flash.display.Graphics; import flash.display.Shape; import flash.geom.Rectangle; import flash.text.TextFormat; import flash.text.engine.EastAsianJustifier; import flash.text.engine.ElementFormat; import flash.text.engine.FontDescription; import flash.text.engine.FontLookup; import flash.text.engine.FontMetrics; import flash.text.engine.Kerning; import flash.text.engine.LineJustification; import flash.text.engine.SpaceJustifier; import flash.text.engine.TextBaseline; import flash.text.engine.TextBlock; import flash.text.engine.TextElement; import flash.text.engine.TextLine; import flash.text.engine.TypographicCase; import flashx.textLayout.compose.ISWFContext; import flashx.textLayout.compose.TextLineRecycler; import flashx.textLayout.formats.BaselineShift; import flashx.textLayout.formats.TLFTypographicCase; import mx.core.IEmbeddedFontRegistry; import mx.core.IFlexModuleFactory; import mx.core.IUIComponent; import mx.core.Singleton; import mx.core.mx_internal; import spark.components.supportClasses.TextBase; import spark.utils.TextUtil; use namespace mx_internal; //-------------------------------------- // Styles //-------------------------------------- include "../styles/metadata/BasicInheritingTextStyles.as" include "../styles/metadata/BasicNonInheritingTextStyles.as" //-------------------------------------- // Other metadata //-------------------------------------- [DefaultProperty("text")] [IconFile("Label.png")] /** * Label is a low-level UIComponent that can render * one or more lines of uniformly-formatted text. * The text to be displayed is determined by the * text property inherited from TextBase. * The formatting of the text is specified by the element's CSS styles, * such as fontFamily and fontSize. * *

Label uses of the * Flash Text Engine (FTE) in Flash Player to provide high-quality * international typography. * Because Label is fast and lightweight, it is especially suitable * for use cases that involve rendering many small pieces of non-interactive * text, such as item renderers and labels in Button skins.

* *

The Spark architecture provides three text "primitives" -- * Label, RichText, and RichEditableText -- * as part of its pay-only-for-what-you-need philosophy. * Label is the fastest and most lightweight, * but is limited in its capabilities: no complex formatting, * no scrolling, no selection, no editing, and no hyperlinks. * RichText and RichEditableText are built on the Text Layout * Framework (TLF) library, rather than on FTE. * RichText adds the ability to render rich HTML-like text * with complex formatting, but is still completely non-interactive. * RichEditableText is the slowest and heaviest, * but can do it all: it supports scrolling with virtualized TextLines, * selection, editing, hyperlinks, and images loaded from URLs. * You should use the fastest one that meets your needs.

* *

The Spark Label control is similar to the MX Label control, mx.controls.Label. * The most important differences are: *

* *

In Spark Label, three character sequences are recognized * as explicit line breaks: CR ("\r"), LF ("\n"), * and CR+LF ("\r\n").

* *

If you don't specify any kind of width for a Label, * then the longest line, as determined by these explicit line breaks, * determines the width of the Label.

* *

If you do specify some kind of width, then the specified text is * word-wrapped at the right edge of the component's bounds, because the * default value of the lineBreak style is "toFit". * If the text extends below the bottom of the component, * it is clipped.

* *

To disable this automatic wrapping, set the lineBreak * style to "explicit". Then lines are broken only where * the text contains an explicit line break, * and the ends of lines extending past the right edge is clipped.

* *

If you have more text than you have room to display it, * Label can truncate the text for you. * Truncating text means replacing excess text * with a truncation indicator such as "...". * See the inherited properties maxDisplayedLines * and isTruncated.

* *

You can control the line spacing with the lineHeight style. * You can horizontally and vertically align the text within the element's * bounds using the textAlign, textAlignLast, * and verticalAlign styles. * You can inset the text from the element's edges using the * paddingLeft, paddingTop, * paddingRight, and paddingBottom styles.

* *

By default a Label has no background, * but you can draw one using the backgroundColor * and backgroundAlpha styles. * Borders are not supported. * If you need a border, or a more complicated background, use a separate * graphic element, such as a Rect, behind the Label.

* *

Label supports displaying left-to-right (LTR) text such as French, * right-to-left (RTL) text such as Arabic, and bidirectional text * such as a French phrase inside of an Arabic paragraph. * If the predominant text direction is right-to-left, * set the direction style to "rtl". * The textAlign style defaults to "start", * which makes the text left-aligned when direction * is "ltr" and right-aligned when direction * is "rtl". * To get the opposite alignment, * set textAlign to "end".

* *

Label uses the TextBlock class in the Flash Text Engine * to create one or more TextLine objects to statically display * its text String in the format determined by its CSS styles. * For performance, its TextLines do not contain information * about individual glyphs; for more info, see * flash.text.engine.TextLineValidity.STATIC.

* *

The Label control has the following default characteristics:

* * * * * *
CharacteristicDescription
Default size0 pixels wide by 12 pixels high if it contains no text, * and large enough ti display the text if it does
Minimum size0 pixels
Maximum size10000 pixels wide and 10000 pixels high
* * @mxml

The <s:Label> tag inherits all of the tag * attributes of its superclass and adds the following tag attributes:

* *
 *  <s:Label 
 *    Properties
 *    fontContext=""
 * 
 *    Styles
 *    alignmentBaseline="baseline"
 *    baselineShift="0"
 *    cffHinting="0.0"
 *    color="0x000000"
 *    digitCase="default"
 *    digitWidth="default"
 *    direction="ltr"
 *    dominantBaseline="auto"
 *    fontFamily="Arial"
 *    fontLookup="embeddedCFF"
 *    fontSize="12"
 *    fontStyle="normal"
 *    fontWeight="normal"
 *    justificationRule="auto"
 *    justificationStyle="auto"
 *    kerning="false"
 *    ligatureLevel="common"
 *    lineBreak="toFit"
 *    lineHeight="120%"
 *    lineThrough="false"
 *    locale="en"
 *    paddingBottom="0"
 *    paddingLeft="0"
 *    paddingRight="0"
 *    paddingTop="0"
 *    renderingMode="cff"
 *    textAlign="start"
 *    textAlignLast="start"
 *    textAlpha="1"
 *    textDecoration="start"
 *    textJustify="interWord"
 *    trackingLeft="0"
 *    trackingRight="00"
 *    typographicCase="default"
 *    verticalAlign="top"
 *  />
 *  
* * @see spark.components.RichEditableText * @see spark.components.RichText * @see flash.text.engine.TextLineValidity#STATIC * * @includeExample examples/LabelExample.mxml * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ public class Label extends TextBase { include "../core/Version.as"; //-------------------------------------------------------------------------- // // Class initialization // //-------------------------------------------------------------------------- /** * @private */ private static function initClass():void { staticTextBlock = new TextBlock(); staticTextElement = new TextElement(); staticSpaceJustifier = new SpaceJustifier(); staticEastAsianJustifier = new EastAsianJustifier(); if ("recreateTextLine" in staticTextBlock) recreateTextLine = staticTextBlock["recreateTextLine"]; } initClass(); //-------------------------------------------------------------------------- // // Class variables // //-------------------------------------------------------------------------- // We can re-use single instances of a few FTE classes over and over, // since they just serve as a factory for the TextLines that we care about. /** * @private */ private static var staticTextBlock:TextBlock; /** * @private */ private static var staticTextElement:TextElement; /** * @private */ private static var staticSpaceJustifier:SpaceJustifier; /** * @private */ private static var staticEastAsianJustifier:EastAsianJustifier; /** * @private * A reference to the recreateTextLine() method in staticTextBlock, * if it exists. This method was added in player 10.1. * It allows better performance by making it possible to reuse * existing TextLines instead of having to create new ones. */ private static var recreateTextLine:Function; //-------------------------------------------------------------------------- // // Class properties // //-------------------------------------------------------------------------- //---------------------------------- // embeddedFontRegistry //---------------------------------- /** * @private * Storage for the _embeddedFontRegistry property. * Note: This gets initialized on first access, * not when this class is initialized, in order to ensure * that the Singleton registry has already been initialized. */ private static var _embeddedFontRegistry:IEmbeddedFontRegistry; /** * @private * A reference to the embedded font registry. * Single registry in the system. * Used to look up the moduleFactory of a font. */ private static function get embeddedFontRegistry():IEmbeddedFontRegistry { if (!_embeddedFontRegistry) { _embeddedFontRegistry = IEmbeddedFontRegistry( Singleton.getInstance("mx.core::IEmbeddedFontRegistry")); } return _embeddedFontRegistry; } //-------------------------------------------------------------------------- // // Class methods // //-------------------------------------------------------------------------- /** * @private */ private static function getNumberOrPercentOf(value:Object, n:Number):Number { // If 'value' is a Number like 10.5, return it. if (value is Number) return Number(value); // If 'value' is a percentage String like "10.5%", // return that percentage of 'n'. if (value is String) { var len:int = String(value).length; if (len >= 1 && value.charAt(len - 1) == "%") { var percent:Number = Number(value.substring(0, len - 1)); return percent / 100 * n; } } // Otherwise, return NaN. return NaN; } //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructor. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ public function Label() { super(); } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * @private * Holds the last recorded value of the module factory * used to create the font. */ private var embeddedFontContext:IFlexModuleFactory; /** * @private * When we render the text using FTE, this object represents the formatting * for our text element(s). Every time format related styles change, this * object is released because it is invalid. It is regenerated just in time * to render the text. */ private var elementFormat:ElementFormat; //-------------------------------------------------------------------------- // // Overidden Methods: ISimpleStyleClient // //-------------------------------------------------------------------------- /** * @private */ override public function stylesInitialized():void { super.stylesInitialized(); elementFormat = null; } /** * @private */ override public function styleChanged(styleProp:String):void { super.styleChanged(styleProp); elementFormat = null; } //-------------------------------------------------------------------------- // // Overridden methods: TextBase // //-------------------------------------------------------------------------- /** * @private * This helper method is used by measure() and updateDisplayList(). * It composes TextLines to render the 'text' String, * using the staticTextBlock as a factory, * and using the 'width' and 'height' parameters to define the size * of the composition rectangle, with NaN meaning "no limit". * It stops composing when the composition rectangle has been filled. * Returns true if all lines were composed, otherwise false. */ override mx_internal function composeTextLines(width:Number = NaN, height:Number = NaN):Boolean { super.composeTextLines(width, height); if (!elementFormat) elementFormat = createElementFormat(); // Set the composition bounds to be used by createTextLines(). // If the width or height is NaN, it will be computed by this method // by the time it returns. // The bounds are then used by the addTextLines() method // to determine the isOverset flag. // The composition bounds are also reported by the measure() method. bounds.x = 0; bounds.y = 0; bounds.width = width; bounds.height = height; // Remove the TextLines from the container and then release them for // reuse, if supported by the player. removeTextLines(); releaseTextLines(); // Create the TextLines. var allLinesComposed:Boolean = createTextLines(elementFormat); // Need truncation if all the following are true // - there is text (even if there is no text there is may be padding // which may not fit and the text would be reported as truncated) // - truncation options exist (0=no trunc, -1=fill up bounds then trunc, // n=n lines then trunc) // - compose width is specified // - content doesn't fit var lb:String = getStyle("lineBreak"); if (text != null && text.length > 0 && maxDisplayedLines && !doesComposedTextFit(height, width, allLinesComposed, maxDisplayedLines, lb)) { truncateText(width, height, lb); } // Detach the TextLines from the TextBlock that created them. releaseLinesFromTextBlock(); // Add the new text lines to the container. addTextLines(); // Figure out if a scroll rect is needed. isOverset = isTextOverset(width, height); // Just recomposed so reset. invalidateCompose = false; return allLinesComposed; } //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- /** * @private * Creates an ElementFormat (and its FontDescription) * based on the Label's CSS styles. * These must be recreated each time because FTE * does not allow them to be reused. * As a side effect, this method also sets embeddedFontContext * so that we know which SWF should be used to create TextLines. * (TextLines using an embedded font must be created in the SWF * where the font is.) */ private function createElementFormat():ElementFormat { // When you databind to a text formatting style on a Label, // as in