“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″ /]