Truncate Spark Label in the middle and showTruncationTip

Found a little handy customization of the Spark Label control that truncates the label in the middle with a (…). However it didn’t show the full label text on roll over when setting the showTruncationTip=”true” property of the control. I added a few little lines of code to fix it.

The code stores the original value in a variable called “_trueText”, so that just needs to be the value you pass to the toolTip when showTruncationTip=”true”. You need to override the mx_internal function to accomplish this.

override mx_internal function setIsTruncated(value:Boolean):void
		{
			if (_isTruncated != value)
			{
				_isTruncated = value;
				if (showTruncationTip)
					toolTip = _isTruncated ? _trueText : null;
				dispatchEvent(new Event("isTruncatedChanged"));
			}
		}

Then you need to add one little bit of code in the truncateTextMiddle method that will tell the control that the text is indeed truncated, setIsTruncated(true); should be at the end of that method around line 153 in the code snippet below:

	public function truncateTextMiddle(fullText:String, widthToTruncate:Number) : String
		{
			if (!(fullText) || fullText.length < 3 || !this.parent)
			{
				// skip any truncating if no styles (no parent),
				// or text is too small
				return fullText;
			}
			
			// add paddings for some font oversize issues
			var paddingWidth:Number =
				UITextField.mx_internal::TEXT_WIDTH_PADDING +
				this.getStyle("paddingLeft") + this.getStyle("paddingRight");
			
			// Skip if width is too small
			if (widthToTruncate < paddingWidth + 10) return fullText;
			
			// Prepare measurement object
			// We create new TextField, and copy styles for it from this object
			// We cannot re-use internal original text field instance because
			// it will cause event firing in process of text measurement
			var measurementField:TextField = new TextField();
			
			// Clear so measured text will not get old styles.
			measurementField.text = "";
			
			// Copy styles into TextField
			var textStyles:UITextFormat = this.determineTextFormatFromStyles();
			measurementField.defaultTextFormat = textStyles;
			var sm:ISystemManager = this.systemManager;
			if (textStyles.font)
			{
				measurementField.embedFonts = sm != null && sm.isFontFaceEmbedded(textStyles);
			}
			else
			{
				measurementField.embedFonts = false;
			}
			if (textStyles.antiAliasType) {
				measurementField.antiAliasType = textStyles.antiAliasType;
			}
			if (textStyles.gridFitType) {
				measurementField.gridFitType = textStyles.gridFitType;
			}
			if (!isNaN(textStyles.sharpness)) {
				measurementField.sharpness = textStyles.sharpness;
			}
			if (!isNaN(textStyles.thickness)) {
				measurementField.thickness = textStyles.thickness;
			}
			
			// Perform initial measure of text and check if need truncating at all
			
			// To measure text, we set it to measurement text field
			// and get line metrics for first line
			measurementField.text = fullText;
			var fullTextWidth:Number = measurementField.getLineMetrics(0).width + paddingWidth;
			if(fullTextWidth > widthToTruncate){
				// get width of ...
				measurementField.text = "...";
				var dotsWidth:Number = measurementField.getLineMetrics(0).width;
				
				// Find out what is the half of truncated text without ...
				var halfWidth : Number = (widthToTruncate - paddingWidth - dotsWidth) / 2;
				
				// Make a rough estimate of how much chars we need to cut out
				// This saves steps of character-by-character preocessing
				measurementField.text = "s";
				var charWidth:Number = measurementField.getLineMetrics(0).width;
				var charsToTruncate:int = Math.round(
					((fullTextWidth - paddingWidth) / 2 - halfWidth) /
					charWidth) + 2;
				
				// allow some distortion to account fractional widths part
				halfWidth = halfWidth - 0.5;
				
				// Below algorithm makes rough middle-truncating
				// Then it is corrected by adding or removing
				// characters for each part until reach required
				// width for each half. Algorith does checks
				// (min max and loop ciodnitions) so that string
				// cannot be less then one character for each half
				
				// see if right part of text approximately fits into half width
				var rightPart:String;
				var widthWithNextChar:Number;
				
				var len:int = fullText.length;
				var currLoc:int = Math.min(len/2 + charsToTruncate + 1, len-1);
				measurementField.text = fullText.substr(currLoc);
				var rightPartWidth:Number = measurementField.getLineMetrics(0).width;
				
				if (rightPartWidth > halfWidth) {
					// throw away characters until fits
					currLoc++;
					while (rightPartWidth > halfWidth && currLoc < len) {
						measurementField.text = fullText.charAt(currLoc);
						rightPartWidth -= measurementField.getLineMetrics(0).width;
						currLoc++;
					}
					rightPart = fullText.substr(currLoc - 1);
				} else {
					// try to add characters one-by-one and
					// see if it still fits
					widthWithNextChar = 0;
					do {
						currLoc--;
						rightPartWidth += widthWithNextChar;
						measurementField.text = fullText.charAt(currLoc);
						widthWithNextChar = measurementField.getLineMetrics(0).width;
					} while (rightPartWidth + widthWithNextChar <= halfWidth && currLoc > 0);
					rightPart = fullText.substr(currLoc + 1);
				}
				
				// Do the same with left part, but compare overall string
				// Overall is needed because character-by character
				// would not give us correct total width of string -
				// somehow overall text is measured with sapcers etc. and
				// also there are rounding issues.
				// This way, and by putting left part calculating as last, we allow
				// left part might be larger (may become more than half).
				
				// allow some distortion in widths fractions
				widthToTruncate = widthToTruncate - 0.5 - paddingWidth;
				
				currLoc = Math.max(len/2 - charsToTruncate, 1);
				measurementField.text = fullText.substr(0, currLoc) +
					"..." + rightPart;
				var truncatedWidth:Number = measurementField.getLineMetrics(0).width;
				if (truncatedWidth > widthToTruncate) {
					// throw away characters until fits
					currLoc--;
					while (truncatedWidth > widthToTruncate && currLoc > 0) {
						measurementField.text = fullText.substr(0, currLoc) +
							"..." + rightPart;
						truncatedWidth = measurementField.getLineMetrics(0).width;
						currLoc--;
					}
					currLoc++;
				} else {
					// try to add characters one-by-one and
					// see if it still fits
					do {
						currLoc++;
						measurementField.text = fullText.substr(0, currLoc) +
							"..." + rightPart;
						widthWithNextChar = measurementField.getLineMetrics(0).width;
					} while (widthWithNextChar <= widthToTruncate &&
						currLoc < len-1);
					currLoc--;
				}

				setIsTruncated(true);
				
				return fullText.substr(0, Math.max(currLoc,1)) +
					"..." + rightPart;
			}
			return fullText;
			
		}

The original post can be found here.

-Mr