“FontBox” – ComboBox for Font Selection

Many moons ago someone over at the Kirupa forum asked how to create a combo box that could display a list of fonts in their “natural state”. Sounded pretty interesting so I whipped together a quick example that made use of some graphics in the .fla library. It was nice and worked, but I thought I’d revisit this idea and rework it so no custom graphics were necessary. Unfortunately, it still requires fonts to be included in the .fla library which, of course, jacks up the file size, but that can’t really be avoided. The final result is here:

the graphics class

package com.onebyonedesign.ui.graphics {     

	import flash.display.CapsStyle; 
	import flash.display.LineScaleMode; 
	import flash.display.SimpleButton; 
	import flash.display.Sprite;     

	/** 
	* Creates graphic display for UI combo box 
	* @author Devon O. 
	* @date 3/2/2008 8:50 AM 
	*/ 
	public class ComboBoxGraphic extends Sprite {     

		private var _w:int; 
		private var _h:int; 
		private var _button:SimpleButton; 
		private var _arrow:Sprite;     

		public function ComboBoxGraphic(width:int, height:int):void { 
			_w = width; 
			_h = height;     

			drawBox(); 
			addButton(); 
			addArrow(); 
		}     

		private function drawBox():void { 
			graphics.lineStyle(1, 0x848484, 1, false, LineScaleMode.NONE, CapsStyle.SQUARE); 
			graphics.moveTo(0, _h - 1); 
			graphics.lineTo(0, 0); 
			graphics.lineTo(_w, 0); 
			graphics.lineStyle(1, 0xE3DDDD, 1, false, LineScaleMode.NONE, CapsStyle.SQUARE); 
			graphics.moveTo(_w, 1); 
			graphics.lineTo(_w, _h - 1); 
			graphics.lineStyle(1, 0xE9E9E9, 1, false, LineScaleMode.NONE, CapsStyle.SQUARE); 
			graphics.moveTo(_w, _h); 
			graphics.lineTo(0, _h); 
			graphics.lineStyle(1, 0xE3DDDD, 1, false, LineScaleMode.NONE, CapsStyle.SQUARE); 
			graphics.moveTo(1, 1); 
			graphics.lineTo(_w - 1, 1); 
			graphics.lineTo(_w - 1, _h - 1); 
			graphics.lineTo(1, _h - 1); 
			graphics.lineTo(1, 1); 
			cacheAsBitmap = true; 
		}     

		private function addButton():void { 
			var btnSize:int = _h - 4;     

			var upstate:Sprite = new Sprite(); 
			upstate.graphics.beginFill(0xFFFFFF); 
			upstate.graphics.lineStyle(1, 0xE3DDDD, 1, false, LineScaleMode.NONE, CapsStyle.SQUARE); 
			upstate.graphics.moveTo(0, 0); 
			upstate.graphics.lineTo(btnSize - 1, 0); 
			upstate.graphics.lineTo(btnSize - 1, btnSize - 1); 
			upstate.graphics.lineTo(0, btnSize - 1); 
			upstate.graphics.lineTo(0, 0); 
			upstate.graphics.endFill(); 
			// shadow 
			upstate.graphics.lineStyle(1, 0x848484, 1, false, LineScaleMode.NONE, CapsStyle.SQUARE); 
			upstate.graphics.moveTo(0, btnSize); 
			upstate.graphics.lineTo(btnSize, btnSize); 
			upstate.graphics.lineTo(btnSize, 0); 
			upstate.cacheAsBitmap = true;     

			var overstate:Sprite = new Sprite(); 
			overstate.graphics.beginFill(0xF5F5F5); 
			overstate.graphics.lineStyle(1, 0xE3DDDD, 1, false, LineScaleMode.NONE, CapsStyle.SQUARE); 
			overstate.graphics.moveTo(0, 0); 
			overstate.graphics.lineTo(btnSize - 1, 0); 
			overstate.graphics.lineTo(btnSize - 1, btnSize - 1); 
			overstate.graphics.lineTo(0, btnSize - 1); 
			overstate.graphics.lineTo(0, 0); 
			overstate.graphics.endFill(); 
			// shadow 
			overstate.graphics.lineStyle(1, 0x848484, 1, false, LineScaleMode.NONE, CapsStyle.SQUARE); 
			overstate.graphics.moveTo(0, btnSize); 
			overstate.graphics.lineTo(btnSize, btnSize); 
			overstate.graphics.lineTo(btnSize, 0); 
			overstate.cacheAsBitmap = true;     

			var downstate:Sprite = new Sprite(); 
			downstate.graphics.beginFill(0xF5F5F5); 
			downstate.graphics.lineStyle(1, 0x848484, 1, false, LineScaleMode.NONE, CapsStyle.SQUARE); 
			downstate.graphics.moveTo(0, btnSize - 1); 
			downstate.graphics.lineTo(0, 0); 
			downstate.graphics.lineTo(btnSize - 1, 0); 
			downstate.graphics.lineStyle(1, 0xE3DDDD, 1, false, LineScaleMode.NONE, CapsStyle.SQUARE); 
			downstate.graphics.lineTo(btnSize - 1, btnSize - 1); 
			downstate.graphics.lineTo(0, btnSize - 1); 
			downstate.graphics.endFill(); 
			// shadow 
			downstate.graphics.lineStyle(1, 0xE3DDDD, 1, false, LineScaleMode.NONE, CapsStyle.SQUARE); 
			downstate.graphics.moveTo(0, btnSize); 
			downstate.graphics.lineTo(btnSize, btnSize); 
			downstate.graphics.lineTo(btnSize, 0); 
			downstate.cacheAsBitmap = true;     

			var hitstate:Sprite = new Sprite(); 
			hitstate.graphics.beginFill(0x000000); 
			hitstate.graphics.moveTo((btnSize + 1) - _w, -2); 
			hitstate.graphics.lineTo(btnSize + 1, -2); 
			hitstate.graphics.lineTo(btnSize + 1, btnSize + 2); 
			hitstate.graphics.lineTo((btnSize + 1) - _w, btnSize + 2); 
			hitstate.graphics.endFill(); 
			hitstate.cacheAsBitmap = true;     

			_button = new SimpleButton(upstate, overstate, downstate, hitstate);     

			_button.x = _w - btnSize - 1; 
			_button.y = 2; 
			addChild(_button); 
		}     

		private function addArrow():void { 
			_arrow = new Sprite(); 
			_arrow.graphics.beginFill(0x282828); 
			_arrow.graphics.moveTo(0, 0); 
			_arrow.graphics.lineTo(6, 0); 
			_arrow.graphics.lineTo(3, 3); 
			_arrow.graphics.lineTo(0, 0); 
			_arrow.graphics.endFill(); 
			_arrow.cacheAsBitmap = true;     

			_arrow.x = int((_button.x + (_button.width / 2)) - (_arrow.width / 2)); 
			_arrow.y = int((_h / 2) - (_arrow.height / 2)); 
			addChild(_arrow); 
		} 
	} 
}

The event class:

package com.onebyonedesign.ui.events {     

	import flash.events.Event;     

	public class FontBoxEvent extends Event {     

		public static const FONT_SELECTED:String = "onFontSelected";     

		public function FontBoxEvent(type:String):void { 
			super(type); 
		} 
	} 
}

The “main” class:

package com.onebyonedesign.ui {     

	import com.onebyonedesign.ui.events.FontBoxEvent; 
	import com.onebyonedesign.ui.graphics.ComboBoxGraphic; 
	import flash.display.MovieClip; 
	import flash.text.TextField; 
	import flash.text.TextFieldAutoSize; 
	import flash.text.TextFormat; 
	import flash.text.AntiAliasType; 
	import flash.display.Sprite; 
	import flash.events.MouseEvent; 
	import flash.events.Event; 
	import flash.text.Font;     

	/** 
	* Creates combo box that displays font styles in their natural format 
	* @author Devon O. 
	* @date 3/2/2008 8:50 AM 
	*/     

	public dynamic class FontBox extends MovieClip {     

		private var _title:String; 
		private var _initialText:String; 
		private var _fontProvider:Array; 
		private var _boxWidth:int; 
		private var _boxHeight:int; 
		private var _displayFontSize:int; 
		private var _displayFont:String; 
		private var _fontSize:int;     

		private var _titleText:TextField; 
		private var _selectedText:TextField;     

		private var _comboBox:ComboBoxGraphic;     

		private var _choices:Array =  new Array(); 
		private var _choiceHolder:Sprite = new Sprite();; 
		private var _font:Font;     

		/** 
		 * returns the selected font 
		 */ 
		public function get font():Font { 
			return _font; 
		}     

		/** 
		 * 
		 * @param	String that will be displayed as the title of the combobox instance 
		 * @param	String that will be initally displayed inside combobox 
		 * @param	array of Font instances that will be displayed 
		 * @param	width of combobox graphic 
		 * @param	height of combobox graphic 
		 * @param	String name of font used for title and text displayed inside combobox 
		 * @param	size of font displayed in title and inside combobox 
		 * @param	size of fonts displayed in drop down list 
		 */ 
		public function FontBox(title:String, initialDisplayText:String, fontList:Array, width:int = 120, height:int = 18, displayFont:String = "_sans", displayFontSize:int = 11, fontSize:int = 22):void { 
			_title = title; 
			_initialText = initialDisplayText; 
			_fontProvider = fontList; 
			_boxWidth = width; 
			_boxHeight = height; 
			_displayFont = displayFont; 
			_displayFontSize = displayFontSize; 
			_fontSize = fontSize;     

			init(); 
		}     

		private function init():void { 
			createTitle(); 
			createBox(); 
		}     

		private function createTitle():void { 
			_titleText = new TextField(); 
			_titleText.autoSize = TextFieldAutoSize.LEFT; 
			_titleText.selectable = false; 
			_titleText.defaultTextFormat = new TextFormat(_displayFont, _displayFontSize); 
			_titleText.antiAliasType = AntiAliasType.ADVANCED; 
			_titleText.text = _title; 
			addChild(_titleText); 
		}     

		private function createBox():void { 
			_comboBox = new ComboBoxGraphic(_boxWidth, _boxHeight); 
			_comboBox.x = int(_titleText.width + 10); 
			addChild(_comboBox);     

			_selectedText = new TextField(); 
			_selectedText.width = _boxWidth - _boxHeight; 
			_selectedText.selectable = false; 
			_selectedText.mouseEnabled = false; 
			_selectedText.defaultTextFormat = new TextFormat(_displayFont, _displayFontSize); 
			_selectedText.antiAliasType = AntiAliasType.ADVANCED; 
			_selectedText.text = _initialText; 
			_selectedText.x = _comboBox.x + 1; 
			addChild(_selectedText);     

			_comboBox.addEventListener(MouseEvent.CLICK, onClick); 
		}     

		// Events 
		private function onOut(me:MouseEvent):void { 
			removeChoices(); 
			_choices = new Array(); 
		}     

		private function onClick(me:MouseEvent):void { 
			parent.setChildIndex(this, parent.numChildren - 1); 
			addChoices(); 
		}     

		private function onSelect(me:MouseEvent):void { 
			_selectedText.text = me.target.label; 
			_font = me.target.data; 
			dispatchEvent(new FontBoxEvent(FontBoxEvent.FONT_SELECTED)); 
			removeChoices(); 
		}     

		private function addChoices():void { 
			_choiceHolder.x = _comboBox.x; 
			_choiceHolder.y = _comboBox.height; 
			_fontProvider.forEach(addChoice); 
			_choiceHolder.addEventListener(MouseEvent.ROLL_OUT, onOut); 
			addChild(_choiceHolder); 
		}     

		private function addChoice(f:Font, i:int, a:Array):void { 
			var c:Choice = new Choice(f.fontName, f, _boxWidth, _fontSize); 
			_choices.push(c); 
			c.useHandCursor = false; 
			if (i > 0) 
				c.y = _choices[i - 1].y + _choices[i - 1].height; 
			c.addEventListener(MouseEvent.CLICK, onSelect); 
			_choiceHolder.addChild(c); 
		}     

		private function removeChoices():void { 
			if (contains(_choiceHolder)) removeChild(_choiceHolder); 
		} 
	} 
}     

//	Choice class     

import flash.display.SimpleButton; 
import flash.text.TextField; 
import flash.text.TextFormat; 
import flash.text.AntiAliasType; 
import flash.text.Font; 
import flash.display.Sprite;     

class Choice extends SimpleButton {     

	private var _fontLabel:TextField; 
	private var _size:int; 
	private var _width:int; 
	private var _label:String; 
	private var _data:Font; 
	private var _boxHeight:Number;     

	public function get label():String { 
		return _label; 
	} 
	public function get data():Font { 
		return _data; 
	}     

	function Choice (label:String, data:Font, width:int, fontsize:int) { 
		_label = label; 
		_data = data; 
		_size = fontsize; 
		_width = width; 
		_fontLabel = addLabel(); 
		_boxHeight = _fontLabel.height; 
		upState = upSprite(); 
		overState = overSprite(); 
		downState = upSprite(); 
		hitTestState = upSprite(); 
	} 
	private function upSprite():Sprite { 
		var s:Sprite = new Sprite(); 
		s.graphics.beginFill(0xEFEFEF); 
		s.graphics.drawRect(0, 0, _width, _boxHeight); 
		s.graphics.endFill(); 
		s.addChild(addLabel()); 
		return s; 
	} 
	private function overSprite():Sprite { 
		var s:Sprite = new Sprite(); 
		s.graphics.beginFill(0xDFDFDF); 
		s.graphics.drawRect(0, 0, _width, _boxHeight); 
		s.graphics.endFill(); 
		s.addChild(addLabel()); 
		return s; 
	}     

	private function addLabel():TextField { 
		var tf:TextField = new TextField(); 
		tf.antiAliasType = AntiAliasType.ADVANCED; 
		var fmt:TextFormat = new TextFormat(_data.fontName, _size); 
		tf.defaultTextFormat = fmt; 
		tf.embedFonts = true; 
		tf.selectable = false; 
		tf.mouseEnabled = false; 
		tf.text = _label; 
		tf.width = _width - 2; 
		tf.height = tf.textHeight; 
		return tf; 
	} 
}

And finally a document class test:

package {     

	import com.onebyonedesign.ui.events.FontBoxEvent; 
	import com.onebyonedesign.ui.FontBox; 
	import flash.display.Sprite; 
	import flash.text.AntiAliasType; 
	import flash.text.TextField; 
	import flash.text.TextFieldAutoSize; 
	import flash.text.TextFormat;     

	public class Test extends Sprite {     

		private var _fontBox:FontBox; 
		private var _lipsum:String = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas at leo eget nisl porta viverra. Ut laoreet, dui at tempus vestibulum, eros leo egestas neque, id adipiscing odio eros et lectus. Vivamus pretium lorem sit amet nulla. Praesent nec dolor at augue ultrices blandit." 
		private var _exampleText:TextField; 
		private var _format:TextFormat = new TextFormat(new ArialFont().fontName, 12);     

		public function Test():void { 
			initExampleText(); 
			initFontBox(); 
		}     

		private function initFontBox():void {     

			//	ArialFont, ComicFont, CourierFont and TimesFont are fonts included in .fla library     

			var fontArray:Array = new Array(new ArialFont(), new ComicFont(), new CourierFont(), new TimesFont()); 
			_fontBox = new FontBox("Fonts:", "-choose a font-", fontArray, 150); 
			_fontBox.x = 50; 
			_fontBox.y = 50; 
			_fontBox.addEventListener(FontBoxEvent.FONT_SELECTED, onFontSelect); 
			addChild(_fontBox); 
		}     

		private function initExampleText():void { 
			_exampleText = new TextField(); 
			_exampleText.multiline = true; 
			_exampleText.wordWrap = true; 
			_exampleText.autoSize = TextFieldAutoSize.LEFT; 
			_exampleText.antiAliasType = AntiAliasType.ADVANCED; 
			_exampleText.width = 300; 
			_exampleText.embedFonts = true; 
			_exampleText.defaultTextFormat = _format; 
			_exampleText.text = _lipsum; 
			_exampleText.x = 50; 
			_exampleText.y = 100; 
			addChild(_exampleText); 
		}     

		private function onFontSelect(fbe:FontBoxEvent):void { 
			_format.font = _fontBox.font.fontName; 
			_exampleText.setTextFormat(_format); 
		} 
	}     

}

which yields:

[kml_flashembed movie=”http://blog.onebyonedesign.com/wp-content/uploads/2008/03/fontboxexample.swf” height=”300″ width=”500″ /]

Date: