# Adding virtual keyboard support for interactive objects

Normally, the virtual keyboard only opens when a TextField object is tapped. You
can configure an instance of the InteractiveObject class to open the virtual
keyboard when it receives focus.

To configure an InteractiveObject instance to open the soft keyboard, set its
`needsSoftKeyboard` property to `true`. Whenever the object is assigned to the
stage focus property, the soft keyboard automatically opens. In addition, you
can raise the keyboard by calling the `requestSoftKeyboard()` method of the
InteractiveObject.

The following example illustrates how you can program an InteractiveObject to
act as a text entry field. The TextInput class shown in the example sets the
`needsSoftKeyboard` property so that the keyboard is raised when needed. The
object then listens for `keyDown` events and inserts the typed character into
the field.

The example uses the Flash text engine to append and display any typed text and
handles some important events. For simplicity, the example does not implement a
full-featured text field.

    package  {
    	import flash.geom.Rectangle;
    	import flash.display.Sprite;
    	import flash.text.engine.TextElement;
    	import flash.text.engine.TextBlock;
    	import flash.events.MouseEvent;
    	import flash.events.FocusEvent;
    	import flash.events.KeyboardEvent;
    	import flash.text.engine.TextLine;
    	import flash.text.engine.ElementFormat;
    	import flash.events.Event;

    	public class TextInput extends Sprite
    	{

    		public var text:String = " ";
    		public  var textSize:Number = 24;
    		public var textColor:uint = 0x000000;
    		private var _bounds:Rectangle = new Rectangle( 0, 0, 100, textSize );
    		private var textElement: TextElement;
    		private var textBlock:TextBlock = new  TextBlock();

    		public function TextInput( text:String = "" )
    		{
    			this.text = text;
    			this.scrollRect = _bounds;
    			this.focusRect= false;

    			//Enable keyboard support
    			this.needsSoftKeyboard = true;
    			this.addEventListener(MouseEvent.MOUSE_DOWN, onSelect);
    			this.addEventListener(FocusEvent.FOCUS_IN, onFocusIn);
    			this.addEventListener(FocusEvent.FOCUS_OUT, onFocusOut);

    			//Setup text engine
    			textElement = new TextElement( text, new ElementFormat( null, textSize, textColor ) );
    			textBlock.content = textElement;
    			var firstLine:TextLine = textBlock.createTextLine( null, _bounds.width - 8 );
    			firstLine.x = 4;
    			firstLine.y = 4 + firstLine.totalHeight;
    			this.addChild( firstLine );

    		}

    		private function onSelect( event:MouseEvent ):void
    		{
    			stage.focus = this;
    		}
    		private function onFocusIn( event:FocusEvent ):void
    		{
    			this.addEventListener( KeyboardEvent.KEY_DOWN, onKey );
    		}

    		private function onFocusOut( event:FocusEvent ):void
    		{
    			this.removeEventListener( KeyboardEvent.KEY_UP, onKey );
    		}

    		private function onKey( event:KeyboardEvent ):void
    		{
    			textElement.replaceText( textElement.text.length, textElement.text.length, String.fromCharCode( event.charCode ) );
    			updateText();
    		}
    		public function set bounds( newBounds:Rectangle ):void
    		{
    			_bounds = newBounds.clone();
    			drawBackground();
    			updateText();
    			this.scrollRect = _bounds;

    			//force update to focus rect, if needed
    			if( this.stage!= null && this.focusRect && this.stage.focus == this )
    				this.stage.focus = this;
    		}

    		private function updateText():void
    		{
    			//clear text lines
    			while( this.numChildren > 0 ) this.removeChildAt( 0 );

    			//and recreate them
    			var textLine:TextLine = textBlock.createTextLine( null, _bounds.width - 8);
    			while ( textLine)
    			{
    				textLine.x = 4;
    				if( textLine.previousLine != null )
    				{
    					textLine.y = textLine.previousLine.y +
    								textLine.previousLine.totalHeight + 2;
    				}
    								else
    				{
    					textLine.y = 4 + textLine.totalHeight;
    				}
    				this.addChild(textLine);
    				textLine = textBlock.createTextLine(textLine, _bounds.width - 8 );
    				}
    		}

    		private function drawBackground():void
    		{
    			//draw background and border for the field
    			this.graphics.clear();
    			this.graphics.beginFill( 0xededed );
    			this.graphics.lineStyle( 1, 0x000000 );
    			this.graphics.drawRect( _bounds.x + 2, _bounds.y + 2, _bounds.width - 4, _bounds.height - 4);
    			this.graphics.endFill();
    		}
    	}
    }

The following main application class illustrates how to use the TextInput class
and manage the application layout when the keyboard is raised or the device
orientation changes. The main class creates a TextInput object and sets its
bounds to fill the stage. The class adjusts the size of the TextInput object
when either the soft keyboard is raised or the stage changes size. The class
listens for soft keyboard events from the TextInput object and resize events
from the stage. Irrespective of the cause of the event, the application
determines the visible area of the stage and resizes the input control to fill
it. Naturally, in a real application, you would need a more sophisticated layout
algorithm.

    package  {

    	import flash.display.MovieClip;
    	import flash.events.SoftKeyboardEvent;
    	import flash.geom.Rectangle;
    	import flash.events.Event;
    	import flash.display.StageScaleMode;
    	import flash.display.StageAlign;

    	public class CustomTextField extends MovieClip {

    		private var customField:TextInput = new TextInput("Input text: ");

    		public function CustomTextField() {
    			this.stage.scaleMode = StageScaleMode.NO_SCALE;
    			this.stage.align = StageAlign.TOP_LEFT;
    			this.addChild( customField );
    			customField.bounds = new Rectangle( 0, 0, this.stage.stageWidth, this.stage.stageHeight );

    			//track soft keyboard and stage resize events
    			customField.addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATE, onDisplayAreaChange );
    			customField.addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_DEACTIVATE, onDisplayAreaChange );
    			this.stage.addEventListener( Event.RESIZE, onDisplayAreaChange );
    		}

    		private function onDisplayAreaChange( event:Event ):void
    		{
    			//Fill the stage if possible, but avoid the area covered by a keyboard
    			var desiredBounds = new Rectangle( 0, 0, this.stage.stageWidth, this.stage.stageHeight );
    			if( this.stage.stageHeight - this.stage.softKeyboardRect.height < desiredBounds.height )
    				desiredBounds.height = this.stage.stageHeight - this.stage.softKeyboardRect.height;

    			customField.bounds = desiredBounds;
    		}
    	}
    }

> **Note:** The stage only dispatches resize events in response to an
> orientation change when the `scaleMode` property is set to `noScale`. In other
> modes, the dimensions of the stage do not change; instead, the content is
> scaled to compensate.
