Skip to content

Instantly share code, notes, and snippets.

@moly
Created August 25, 2012 12:26
Show Gist options
  • Save moly/3464881 to your computer and use it in GitHub Desktop.
Save moly/3464881 to your computer and use it in GitHub Desktop.
Flixel's FlxText class modified to accept input. Written for flixel 2.35
package org.flixel
{
import flash.display.BitmapData;
import flash.events.Event;
import flash.events.FocusEvent;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.text.TextFieldType;
import flash.text.TextFormat;
import flash.ui.Keyboard;
/**
* Extends <code>FlxSprite</code> to support rendering text.
* Can tint, fade, rotate and scale just like a sprite.
* Doesn't really animate though, as far as I know.
* Also does nice pixel-perfect centering on pixel fonts
* as long as they are only one liners.
*/
public class FlxText extends FlxSprite
{
protected var _tf:TextField;
protected var _regen:Boolean;
protected var _shadow:uint;
protected var _initialized:Boolean;
protected var _elapsed:Number;
protected var _caretBlinkSpeed:int;
protected var _showCaret:Boolean;
protected var _showHint:Boolean;
/**
* Creates a new <code>FlxText</code> object at the specified position.
*
* @param X The X position of the text.
* @param Y The Y position of the text.
* @param Width The width of the text object (height is determined automatically).
* @param Text The actual text you would like to display initially.
* @param EmbeddedFont Whether this text field uses embedded fonts or nto
*/
public function FlxText(X:Number, Y:Number, Width:uint, Text:String=null, EmbeddedFont:Boolean=true)
{
super(X,Y);
createGraphic(Width,1,0);
if(Text == null)
Text = "";
_tf = new TextField();
_tf.width = Width;
_tf.embedFonts = EmbeddedFont;
_tf.selectable = false;
_tf.sharpness = 100;
_tf.multiline = true;
_tf.wordWrap = true;
_tf.text = Text;
var tf:TextFormat = new TextFormat("system",8,0xffffff);
_tf.defaultTextFormat = tf;
_tf.setTextFormat(tf);
if(Text.length <= 0)
_tf.height = 1;
else
_tf.height = 10;
_caretBlinkSpeed = 500;
_showCaret = true;
_elapsed = 0;
_tf.addEventListener(FocusEvent.FOCUS_IN, onTextFieldFocusIn);
_tf.addEventListener(FocusEvent.FOCUS_OUT, onTextFieldFocusOut);
_tf.addEventListener(Event.CHANGE, onChange);
_tf.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
_regen = true;
_shadow = 0;
solid = false;
calcFrame();
}
private function onChange(e:Event):void
{
calcFrame();
}
override public function update():void
{
if(!_initialized && type == TextFieldType.INPUT)
{
if(FlxG.stage != null)
{
FlxG.stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
_tf.multiline = false;
_initialized = true;
}
}
super.update();
if (type == TextFieldType.INPUT)
{
_elapsed += FlxG.elapsed;
if (_elapsed * 1000 >= _caretBlinkSpeed)
{
_showCaret = !_showCaret;
_elapsed = 0;
calcFrame();
}
}
}
/**
* You can use this if you have a lot of text parameters
* to set instead of the individual properties.
*
* @param Font The name of the font face for the text display.
* @param Size The size of the font (in pixels essentially).
* @param Color The color of the text in traditional flash 0xRRGGBB format.
* @param Alignment A string representing the desired alignment ("left,"right" or "center").
* @param ShadowColor A uint representing the desired text shadow color in flash 0xRRGGBB format.
*
* @return This FlxText instance (nice for chaining stuff together, if you're into that).
*/
public function setFormat(Font:String=null,Size:Number=8,Color:uint=0xffffff,Alignment:String=null,ShadowColor:uint=0):FlxText
{
if(Font == null)
Font = "";
var tf:TextFormat = dtfCopy();
tf.font = Font;
tf.size = Size;
tf.color = Color;
tf.align = Alignment;
_tf.defaultTextFormat = tf;
_tf.setTextFormat(tf);
_shadow = ShadowColor;
_regen = true;
calcFrame();
return this;
}
/**
* The text being displayed.
*/
public function get text():String
{
return _tf.text;
}
/**
* @private
*/
public function set text(Text:String):void
{
var ot:String = _tf.text;
_tf.text = Text;
if(_tf.text != ot)
{
_regen = true;
calcFrame();
}
}
/**
* The size of the text being displayed.
*/
public function get size():Number
{
return _tf.defaultTextFormat.size as Number;
}
/**
* @private
*/
public function set size(Size:Number):void
{
var tf:TextFormat = dtfCopy();
tf.size = Size;
_tf.defaultTextFormat = tf;
_tf.setTextFormat(tf);
_regen = true;
calcFrame();
}
/**
* The color of the text being displayed.
*/
override public function get color():uint
{
return _tf.defaultTextFormat.color as uint;
}
/**
* @private
*/
override public function set color(Color:uint):void
{
var tf:TextFormat = dtfCopy();
tf.color = Color;
_tf.defaultTextFormat = tf;
_tf.setTextFormat(tf);
_regen = true;
calcFrame();
}
/**
* The font used for this text.
*/
public function get font():String
{
return _tf.defaultTextFormat.font;
}
/**
* @private
*/
public function set font(Font:String):void
{
var tf:TextFormat = dtfCopy();
tf.font = Font;
_tf.defaultTextFormat = tf;
_tf.setTextFormat(tf);
_regen = true;
calcFrame();
}
/**
* The alignment of the font ("left", "right", or "center").
*/
public function get alignment():String
{
return _tf.defaultTextFormat.align;
}
/**
* @private
*/
public function set alignment(Alignment:String):void
{
var tf:TextFormat = dtfCopy();
tf.align = Alignment;
_tf.defaultTextFormat = tf;
_tf.setTextFormat(tf);
calcFrame();
}
/**
* The alignment of the font ("left", "right", or "center").
*/
public function get shadow():uint
{
return _shadow;
}
/**
* @private
*/
public function set shadow(Color:uint):void
{
_shadow = Color;
calcFrame();
}
/**
* Specifies whether the text field has a background fill. If true, the
* text field has a background fill. If false, the textfield has no background fill.
*/
public function set background(value:Boolean):void
{
_tf.background = value;
calcFrame();
}
public function get background():Boolean
{
return _tf.background;
}
/**
* The type of the text field.
* Either one of the following TextFieldType constants: TextFieldType.DYNAMIC, TextFieldType.INPUT
*/
public function set type(value:String):void
{
_tf.type = value;
}
public function get type():String
{
return _tf.type;
}
/**
* The speed at which the caret blinks if this is a input text field, in milliseconds.
*/
public function set caretBlinkSpeed(value:int):void
{
_caretBlinkSpeed = value;
}
public function get caretBlinkSpeed():int
{
return _caretBlinkSpeed;
}
/**
* Sets a hint for input text fields
*/
public function setHint(value:String, colour:uint = 0x888888):void
{
_tf.text = value;
_tf.setTextFormat(new TextFormat(null, null, colour));
_showHint = true;
_regen = true;
calcFrame();
}
private function onTextFieldFocusIn(e:FocusEvent):void
{
if (_showHint)
{
_tf.text = "";
_showHint = false;
}
// disable hot keys when input text field has focus, to avoid having the game mute etc when typing
if (type == TextFieldType.INPUT)
FlxGame.useDefaultHotKeys = false;
}
private function onTextFieldFocusOut(e:FocusEvent):void
{
// re-enable hot keys when focus goes back to the game
FlxGame.useDefaultHotKeys = true;
}
override public function destroy():void
{
super.destroy();
_tf.removeEventListener(FocusEvent.FOCUS_IN, onTextFieldFocusIn);
_tf.removeEventListener(FocusEvent.FOCUS_OUT, onTextFieldFocusOut);
_tf.removeEventListener(Event.CHANGE, onChange);
if (FlxG.stage != null)
{
FlxG.stage.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
FlxG.stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
}
}
/**
* Internal function to update the current animation frame.
*/
override protected function calcFrame():void
{
if(_regen)
{
//Need to generate a new buffer to store the text graphic
var nl:uint = _tf.numLines;
height = 0;
for(var i:uint = 0; i < nl; i++)
height += _tf.getLineMetrics(i).height;
height += 4; //account for 2px gutter on top and bottom
_pixels = new BitmapData(width,height,true,0x0);
_bbb = new BitmapData(width,height,true,0x0);
frameHeight = height;
_tf.height = height*1.2;
_flashRect.x = 0;
_flashRect.y = 0;
_flashRect.width = width;
_flashRect.height = height;
_regen = false;
}
else //Else just clear the old buffer before redrawing the text
_pixels.fillRect(_flashRect, 0);
if((_tf != null) && (_tf.text != null))
{
//Now that we've cleared a buffer, we need to actually render the text to it
var tf:TextFormat = _tf.getTextFormat();//_tf.defaultTextFormat;
var tfa:TextFormat = tf;
_mtx.identity();
//If it's a single, centered line of text, we center it ourselves so it doesn't blur to hell
if((tf.align == "center") && (_tf.numLines == 1))
{
tfa = new TextFormat(tf.font,tf.size,tf.color,null,null,null,null,null,"left");
_tf.setTextFormat(tfa);
_mtx.translate(Math.floor((width - _tf.getLineMetrics(0).width)/2),0);
}
//Render a single pixel shadow beneath the text
if(_shadow > 0)
{
_tf.setTextFormat(new TextFormat(tfa.font,tfa.size,_shadow,null,null,null,null,null,tfa.align));
_mtx.translate(1,1);
_pixels.draw(_tf,_mtx,_ct);
_mtx.translate(-1,-1);
_tf.setTextFormat(new TextFormat(tfa.font,tfa.size,tfa.color,null,null,null,null,null,tfa.align));
}
//Actually draw the text onto the buffer
_pixels.draw(_tf, _mtx, _ct);
_tf.setTextFormat(new TextFormat(tf.font, tf.size, tf.color, null, null, null, null, null, tf.align));
// If this is an input text field, draw the caret
if (type == TextFieldType.INPUT && FlxG.stage.focus == _tf && _showCaret)
{
var caretRect:Rectangle;
caretRect = _tf.getCharBoundaries(_tf.caretIndex - 1);
if (caretRect == null)
caretRect = new Rectangle(2, 2, 1, _tf.getLineMetrics(0).height);
else
{
caretRect.x += caretRect.width;
caretRect.y = 2;
caretRect.width = 1;
}
_pixels.fillRect(caretRect, 0xFF000000);
}
}
//Finally, update the visible pixels
if((_framePixels == null) || (_framePixels.width != _pixels.width) || (_framePixels.height != _pixels.height))
_framePixels = new BitmapData(_pixels.width,_pixels.height,true,0);
_framePixels.copyPixels(_pixels,_flashRect,_flashPointZero);
if(FlxG.showBounds)
drawBounds();
if(solid)
refreshHulls();
}
/**
* A helper function for updating the <code>TextField</code> that we use for rendering.
*
* @return A writable copy of <code>TextField.defaultTextFormat</code>.
*/
protected function dtfCopy():TextFormat
{
var dtf:TextFormat = _tf.defaultTextFormat;
return new TextFormat(dtf.font,dtf.size,dtf.color,dtf.bold,dtf.italic,dtf.underline,dtf.url,dtf.target,dtf.align);
}
private function onMouseDown(e:MouseEvent):void
{
if(!exists || !visible || !active || !FlxG.mouse.justPressed()) return;
if (overlapsPoint(FlxG.mouse.x, FlxG.mouse.y)) {
FlxG.stage.focus = _tf;
var newCaretPosition:int = _tf.getCharIndexAtPoint(FlxG.mouse.x - x, FlxG.mouse.y - y);
if (newCaretPosition == -1)
newCaretPosition = text.length;
setCaretIndex(newCaretPosition);
}
}
private function onKeyDown(e:KeyboardEvent):void
{
if (type == TextFieldType.INPUT && FlxG.stage.focus == _tf)
{
if (e.keyCode == Keyboard.LEFT)
setCaretIndex(_tf.caretIndex - 1);
else if (e.keyCode == Keyboard.RIGHT)
setCaretIndex(_tf.caretIndex + 1);
}
}
private function setCaretIndex(value:int):void
{
_tf.setSelection(value, value);
_showCaret = true;
_elapsed = 0;
calcFrame();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment