Log in
Please log in or register.
Pages: [1]
  Print  
Author Topic: FlxInputText - An input text field what "fits in" with the Flixel workflow  (Read 1269 times)
nitram_cero (2bam)
Sr. Member
****
Posts: 473



View Profile WWW
« on: Tue, Aug 25, 2009 »

FlxInputText - An input text field what "fits in" with the Flixel workflow

Well...I'm working on hi-score submitting and I found out that I needed to enter the initials of the player.
So I did an almost non-intrusive class to do the job.

See how it looks!

It has some cool features like forcing uppercase, text filtering with regexps, max length , colored border, colored background and such.

For example, if I want a 3-uppercase-letters input textbox I only need to do this:

Code:
initialsInput = layer.add(new FlxInputText(X, Y, Width, Height, "", 0xffffff, null, 24)) as FlxInputText;
initialsInput.setMaxLength(3);
initialsInput.filterMode = FlxInputText.ONLY_ALPHA;
initialsInput.forceUpperCase = true;

//...

var userInitials:String = initialsInput.getText();

Notes
* If it's rendered (visible && exists), it can be written to. Update does nothing. This takes into account cascade-drawing... you can safely use layers or call render() yourself.
* The default background color is the negated value of the text color, and the border color the same as the text color. They are both visible by default.
* There is no filtering by default.
* Filtering can be customized by using filterMode=CUSTOM_FILTER and the customFilterPattern member.
* Forcing uppercase is disabled by default.
* Works with zoomed games.

Bugs
* The cursor changes to the default "caret" one, but you can still see the Flixel one. That's kind of buggy but should be easy to fix.


You need to change "private" to "protected" in FlxText's "_tf" member variable for this to work.

Copy and paste the following code in a file named FlxInputText.as and leave it wherever you want Smiley
(you will need to change the package if you don't put it at the root of your project)

I hope you like it, good luck! Grin

The class code
Code:
/*
*** FlxInputText v0.9, Input text field extension for Flixel ***

IMPORTANT: You need to modify the FlxText class so that the _tf member is protected instead of private.
New members:
getText()
setMaxLength()

backgroundColor
borderColor
backgroundVisible
borderVisible

forceUpperCase
filterMode
customFilterPattern


Copyright (c) 2009 Martin Sebastian Wain
License: Creative Commons Attribution 3.0 United States
(http://creativecommons.org/licenses/by/3.0/us/)

(A tiny "single line comment" reference in the source code is more than sufficient as attribution :)
 */

package {
import com.adamatomic.flixel.*;
import flash.events.Event;
import flash.text.TextFieldType;

//@desc Input field class that "fits in" with Flixel's workflow
public class FlxInputText extends FlxText {

static public const NO_FILTER:uint = 0;
static public const ONLY_ALPHA:uint = 1;
static public const ONLY_NUMERIC:uint = 2;
static public const ONLY_ALPHANUMERIC:uint = 3;
static public const CUSTOM_FILTER:uint = 4;

//@desc Defines what text to filter. It can be NO_FILTER, ONLY_ALPHA, ONLY_NUMERIC, ONLY_ALPHA_NUMERIC or CUSTOM_FILTER
// (Remember to append "FlxInputText." as a prefix to those constants)
public var filterMode:uint = NO_FILTER;

//@desc This regular expression will filter out (remove) everything that matches. This is activated by setting filterMode = FlxInputText.CUSTOM_FILTER.
public var customFilterPattern:RegExp = /[]*/g;

//@desc If this is set to true, text typed is forced to be uppercase
public var forceUpperCase:Boolean = false;

//@desc Same parameters as FlxText
public function FlxInputText(X:Number, Y:Number, Width:uint, Height:uint, Text:String, Color:uint=0x000000, Font:String=null, Size:uint=8, Justification:String=null, Angle:Number=0)
{
super(X, Y, Width, Height, Text, Color, Font, Size, Justification, Angle);

_tf.selectable = true;
_tf.type = TextFieldType.INPUT;
_tf.background = true;
_tf.backgroundColor = (~Color) & 0xffffff;
_tf.border = true;
_tf.borderColor = Color;
_tf.visible = false;

_tf.addEventListener(Event.ENTER_FRAME, onEnterFrame);
_tf.addEventListener(Event.REMOVED_FROM_STAGE, onInputFieldRemoved);
_tf.addEventListener(Event.CHANGE, onTextChange);
FlxG.state.addChild(_tf);
}

//@desc Boolean flag in case render() is called BEFORE onEnterFrame() (_tf would be always hidden)
// NOTE: I don't think it's necessary, but I'll leave it just in case.
//@param Direction True is Right, False is Left (see static const members RIGHT and LEFT)
private var nextFrameHide:Boolean = false;

override public function render():void
{
_tf.x=x;
_tf.y=y;
_tf.visible = true;
nextFrameHide = false;
}

private function onInputFieldRemoved(event:Event):void
{
//Clean up after ourselves
_tf.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
_tf.removeEventListener(Event.REMOVED, onInputFieldRemoved);
_tf.removeEventListener(Event.CHANGE, onTextChange);
}

private function onEnterFrame(event:Event):void
{
if(nextFrameHide)
_tf.visible=false;
nextFrameHide = true;
}

private function onTextChange(event:Event):void
{
if(forceUpperCase)
_tf.text = _tf.text.toUpperCase();

if(filterMode != NO_FILTER) {
var pattern:RegExp;
switch(filterMode) {
case ONLY_ALPHA: pattern = /[^a-zA-Z]*/g; break;
case ONLY_NUMERIC: pattern = /[^0-9]*/g; break;
case ONLY_ALPHANUMERIC: pattern = /[^a-zA-Z0-9]*/g; break;
case CUSTOM_FILTER: pattern = customFilterPattern; break;
default:
throw new Error("FlxInputText: Unknown filterMode ("+filterMode+")");
}
_tf.text = _tf.text.replace(pattern, "");
}
}

//@desc Text field background color
public function set backgroundColor(Color:uint):void { _tf.backgroundColor = Color; }
//@desc Text field border color
public function set borderColor(Color:uint):void { _tf.borderColor = Color; }
//@desc Shows/hides background
public function set backgroundVisible(Enabled:Boolean):void { _tf.background = Enabled; }
//@desc Shows/hides border
public function set borderVisible(Enabled:Boolean):void { _tf.border = Enabled; }

//@desc Text field background color
public function get backgroundColor():uint { return _tf.backgroundColor; }
//@desc Text field border color
public function get borderColor():uint { return _tf.borderColor; }
//@desc Shows/hides background
public function get backgroundVisible():Boolean { return _tf.background; }
//@desc Shows/hides border
public function get borderVisible():Boolean { return _tf.border; }

//@desc Set the maximum length for the field (e.g. "3" for Arcade type hi-score initials)
//@param Length The maximum length. 0 means unlimited.
public function setMaxLength(Length:uint):void
{
_tf.maxChars = Length;
}

//@desc Get the text the user has typed
public function getText():String
{
return _tf.text;
}

}

}

FlxState source for the See how it looks! demo.
Code:
package game
{
import com.adamatomic.flixel.*;

public class TEST_InputText extends FlxState
{
[Embed(source="../tbam_asd.png")] private var ImgTBAMLogo:Class;
[Embed(source="data/cursor.png")] private var ImgCursor:Class;


private var layer:FlxLayer;


override public function TEST_InputText():void
{
var logo:FlxSprite;
logo = new FlxSprite(ImgTBAMLogo, 0, 0);
logo.x=1;
logo.y=1;
this.add(logo);

FlxG.setCursor(ImgCursor);
FlxG.flash(0xff000000, 1);

this.add(new FlxButton(0,220,new FlxSprite(null,0,0,false,false,104,15,0xff3a5c39),function():void{layer.visible=false;},new FlxSprite(null,0,0,false,false,104,15,0xff729954),new FlxText(0,1,104,10,"Hide layer",0x729954, null, 8, "center"),new FlxText(0,1,104,10,"Hide layer",0xd8eba2, null, 8, "center")));
this.add(new FlxButton(150,220,new FlxSprite(null,0,0,false,false,104,15,0xff3a5c39),function():void{layer.visible=true;},new FlxSprite(null,0,0,false,false,104,15,0xff729954),new FlxText(0,1,104,10,"Show layer",0x729954, null, 8, "center"),new FlxText(0,1,104,10,"Show layer",0xd8eba2, null, 8, "center")));

layer = new FlxLayer;
layer.add(new FlxText(0, 170, 200, 20, "This FlxText is in a layer", 0xffffff));
var layerInput:FlxInputText = layer.add(new FlxInputText(0, 190, 220, 12, "this input textbox is in a layer too", 0xffffff)) as FlxInputText;
layerInput.borderVisible=false;
layerInput.backgroundColor=0x400000;

this.add(new FlxText(50, 50, 200, 24, "This input text has custom filtering\n(can only write .,:; and digits)", 0xffffff));
var customFilterInput:FlxInputText = this.add(new FlxInputText(50, 76, 220, 12, "", 0xffffff)) as FlxInputText;
customFilterInput.customFilterPattern = /[^.,:;0-9]*/g;
customFilterInput.filterMode = FlxInputText.CUSTOM_FILTER;

this.add(layer);

this.add(new FlxText(20, 100, FlxG.width-40, 30, "Enter your initials (max 3 chars, auto uppercase)", 0xffffff, null, 8, "center")) as FlxText;
var initialsInput:FlxInputText = this.add(new FlxInputText((FlxG.width-100)/2, 120, 100, 30, "MSW", 0xffffff, null, 24)) as FlxInputText;
initialsInput.setMaxLength(3);
initialsInput.filterMode = FlxInputText.ONLY_ALPHA;
initialsInput.forceUpperCase = true;
}
}
}

« Last Edit: Tue, Aug 25, 2009 by nitram_cero » Logged

2BAM
I Hate Islands, SpaceCoffee, Finding Her, Caverns, Explosive Cats, The Duke, Run, A View on Relationships, RabbitClock's Quest
george
Newbie
*
Posts: 7


View Profile
« Reply #1 on: Tue, Aug 25, 2009 »

Looks good!  Smiley
Logged
yueprofile
Newbie
*
Posts: 11


View Profile
« Reply #2 on: Sat, Sep 12, 2009 »

cool! I was looking for this Grin

does it support multiline?
Logged
nitram_cero (2bam)
Sr. Member
****
Posts: 473



View Profile WWW
« Reply #3 on: Sun, Sep 13, 2009 »

cool! I was looking for this Grin

does it support multiline?

Smiley
No, I didn't put that in. But you could patch the class by adding this somewhere:
Code:
public function set multiline(value:Boolean):void
{
      _tf.multiline = value;
      _tf.wordWrap = value;
}

public function get multiline():Boolean
{
      return _tf.multiline;
}

And then access your FlxInputText property like this:
Code:
myText.multiline = true;

Best regards
-Martín
Logged

2BAM
I Hate Islands, SpaceCoffee, Finding Her, Caverns, Explosive Cats, The Duke, Run, A View on Relationships, RabbitClock's Quest
yueprofile
Newbie
*
Posts: 11


View Profile
« Reply #4 on: Sun, Sep 13, 2009 »

cool! I was looking for this Grin

......

Thanks man, it's great!
I also patched the password part Grin




Logged
nitram_cero (2bam)
Sr. Member
****
Posts: 473



View Profile WWW
« Reply #5 on: Fri, Apr 23, 2010 »

I know this is an old topic but, did anybody happen to update this class to be Flixel 2.24+ compatible?


Thanks!
Logged

2BAM
I Hate Islands, SpaceCoffee, Finding Her, Caverns, Explosive Cats, The Duke, Run, A View on Relationships, RabbitClock's Quest
Wurmy
Newbie
*
Posts: 9


View Profile WWW
« Reply #6 on: Fri, Apr 23, 2010 »

I know this is an old topic but, did anybody happen to update this class to be Flixel 2.24+ compatible?


Thanks!

Well, I took your code, copy-pasted it in a new Flixel project (using version 2.34, which is the latest stable one right now I believe), changed the super line, added a '_tf.textColor = Color;', and it seems to work just fine.. except for the angle.

Anywho, it seems to work, but I'm sure that somebody else can probably do more with it than I did :p

Code:
/*
*** FlxInputText v0.9, Input text field extension for Flixel ***
(slight modification by Wurmy, seems to work on version 2.34 of Flixel)

IMPORTANT: You need to modify the FlxText class so that the _tf member is protected instead of private.
New members:
getText()
setMaxLength()

backgroundColor
borderColor
backgroundVisible
borderVisible

forceUpperCase
filterMode
customFilterPattern


Copyright (c) 2009 Martin Sebastian Wain
License: Creative Commons Attribution 3.0 United States
(http://creativecommons.org/licenses/by/3.0/us/)

(A tiny "single line comment" reference in the source code is more than sufficient as attribution :)
 */

package {
import org.flixel.*;
//import com.adamatomic.flixel.*;
import flash.events.Event;
import flash.text.TextFieldType;

//@desc Input field class that "fits in" with Flixel's workflow
public class FlxInputText extends FlxText {

static public const NO_FILTER:uint = 0;
static public const ONLY_ALPHA:uint = 1;
static public const ONLY_NUMERIC:uint = 2;
static public const ONLY_ALPHANUMERIC:uint = 3;
static public const CUSTOM_FILTER:uint = 4;

//@desc Defines what text to filter. It can be NO_FILTER, ONLY_ALPHA, ONLY_NUMERIC, ONLY_ALPHA_NUMERIC or CUSTOM_FILTER
// (Remember to append "FlxInputText." as a prefix to those constants)
public var filterMode:uint = NO_FILTER;

//@desc This regular expression will filter out (remove) everything that matches. This is activated by setting filterMode = FlxInputText.CUSTOM_FILTER.
public var customFilterPattern:RegExp = /[]*/g;

//@desc If this is set to true, text typed is forced to be uppercase
public var forceUpperCase:Boolean = false;

//@desc Same parameters as FlxText
public function FlxInputText(X:Number, Y:Number, Width:uint, Height:uint, Text:String, Color:uint=0x000000, Font:String=null, Size:uint=8, Justification:String=null, Angle:Number=0)
{
//super(X, Y, Width, Height, Text, Color, Font, Size, Justification, Angle);
super(X, Y, Width, Text);

_tf.selectable = true;
_tf.type = TextFieldType.INPUT;
_tf.background = true;
_tf.backgroundColor = (~Color) & 0xffffff;
_tf.textColor = Color;
_tf.border = true;
_tf.borderColor = Color;
_tf.visible = false;
_tf.height = Height;
this.height = Height;
this.angle = Angle;

_tf.addEventListener(Event.ENTER_FRAME, onEnterFrame);
_tf.addEventListener(Event.REMOVED_FROM_STAGE, onInputFieldRemoved);
_tf.addEventListener(Event.CHANGE, onTextChange);
FlxG.state.addChild(_tf);
}

//@desc Boolean flag in case render() is called BEFORE onEnterFrame() (_tf would be always hidden)
// NOTE: I don't think it's necessary, but I'll leave it just in case.
//@param Direction True is Right, False is Left (see static const members RIGHT and LEFT)
private var nextFrameHide:Boolean = false;

override public function render():void
{
_tf.x=x;
_tf.y=y;
_tf.visible = true;
nextFrameHide = false;
}

private function onInputFieldRemoved(event:Event):void
{
//Clean up after ourselves
_tf.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
_tf.removeEventListener(Event.REMOVED, onInputFieldRemoved);
_tf.removeEventListener(Event.CHANGE, onTextChange);
}

private function onEnterFrame(event:Event):void
{
if(nextFrameHide)
_tf.visible=false;
nextFrameHide = true;
}

private function onTextChange(event:Event):void
{
if(forceUpperCase)
_tf.text = _tf.text.toUpperCase();

if(filterMode != NO_FILTER) {
var pattern:RegExp;
switch(filterMode) {
case ONLY_ALPHA: pattern = /[^a-zA-Z]*/g; break;
case ONLY_NUMERIC: pattern = /[^0-9]*/g; break;
case ONLY_ALPHANUMERIC: pattern = /[^a-zA-Z0-9]*/g; break;
case CUSTOM_FILTER: pattern = customFilterPattern; break;
default:
throw new Error("FlxInputText: Unknown filterMode ("+filterMode+")");
}
_tf.text = _tf.text.replace(pattern, "");
}
}

//@desc Text field background color
public function set backgroundColor(Color:uint):void { _tf.backgroundColor = Color; }
//@desc Text field border color
public function set borderColor(Color:uint):void { _tf.borderColor = Color; }
//@desc Shows/hides background
public function set backgroundVisible(Enabled:Boolean):void { _tf.background = Enabled; }
//@desc Shows/hides border
public function set borderVisible(Enabled:Boolean):void { _tf.border = Enabled; }

//@desc Text field background color
public function get backgroundColor():uint { return _tf.backgroundColor; }
//@desc Text field border color
public function get borderColor():uint { return _tf.borderColor; }
//@desc Shows/hides background
public function get backgroundVisible():Boolean { return _tf.background; }
//@desc Shows/hides border
public function get borderVisible():Boolean { return _tf.border; }

//@desc Set the maximum length for the field (e.g. "3" for Arcade type hi-score initials)
//@param Length The maximum length. 0 means unlimited.
public function setMaxLength(Length:uint):void
{
_tf.maxChars = Length;
}

//@desc Get the text the user has typed
public function getText():String
{
return _tf.text;
}

}

}
« Last Edit: Fri, Apr 23, 2010 by Wurmy » Logged

~Wurmy
nitram_cero (2bam)
Sr. Member
****
Posts: 473



View Profile WWW
« Reply #7 on: Fri, Apr 30, 2010 »

Cool, thanks man!
I don't need rotation for those so it should work Smiley
Logged

2BAM
I Hate Islands, SpaceCoffee, Finding Her, Caverns, Explosive Cats, The Duke, Run, A View on Relationships, RabbitClock's Quest
Tyranus
Newbie
*
Posts: 39



View Profile WWW
« Reply #8 on: Thu, Aug 12, 2010 »

A little stupid fix to make it work with scroll.


Code:
*
*** FlxInputText v0.92, Input text field extension for Flixel ***
(author Nitram_cero, Martin Sebastian Wain)
(slight modification by Wurmy, seems to work on version 2.34 of Flixel)
(little insignificant update by Tyranus, now works with scroll)

New members:
getText()
setMaxLength()

backgroundColor
borderColor
backgroundVisible
borderVisible

forceUpperCase
filterMode
customFilterPattern


Copyright (c) 2009 Martin Sebastian Wain
License: Creative Commons Attribution 3.0 United States
(http://creativecommons.org/licenses/by/3.0/us/)

(A tiny "single line comment" reference in the source code is more than sufficient as attribution :)
 */

package {
import org.flixel.*;
import flash.events.Event;
import flash.text.TextFieldType;

//@desc Input field class that "fits in" with Flixel's workflow
public class FlxInputText extends FlxText {

static public const NO_FILTER:uint = 0;
static public const ONLY_ALPHA:uint = 1;
static public const ONLY_NUMERIC:uint = 2;
static public const ONLY_ALPHANUMERIC:uint = 3;
static public const CUSTOM_FILTER:uint = 4;

//@desc Defines what text to filter. It can be NO_FILTER, ONLY_ALPHA, ONLY_NUMERIC, ONLY_ALPHA_NUMERIC or CUSTOM_FILTER
// (Remember to append "FlxInputText." as a prefix to those constants)
public var filterMode:uint = NO_FILTER;

//@desc This regular expression will filter out (remove) everything that matches. This is activated by setting filterMode = FlxInputText.CUSTOM_FILTER.
public var customFilterPattern:RegExp = /[]*/g;

//@desc If this is set to true, text typed is forced to be uppercase
public var forceUpperCase:Boolean = false;

//@desc Same parameters as FlxText
public function FlxInputText(X:Number, Y:Number, Width:uint, Height:uint, Text:String, Color:uint=0x000000, Font:String=null, Size:uint=8, Justification:String=null, Angle:Number=0)
{
//super(X, Y, Width, Height, Text, Color, Font, Size, Justification, Angle);
super(X, Y, Width, Text);

_tf.selectable = true;
_tf.type = TextFieldType.INPUT;
_tf.background = true;
_tf.backgroundColor = (~Color) & 0xffffff;
_tf.textColor = Color;
_tf.border = true;
_tf.borderColor = Color;
_tf.visible = false;
_tf.height = Height;
this.height = Height;
this.angle = Angle;

_tf.addEventListener(Event.ENTER_FRAME, onEnterFrame);
_tf.addEventListener(Event.REMOVED_FROM_STAGE, onInputFieldRemoved);
_tf.addEventListener(Event.CHANGE, onTextChange);
FlxG.state.addChild(_tf);
}

//@desc Boolean flag in case render() is called BEFORE onEnterFrame() (_tf would be always hidden)
// NOTE: I don't think it's necessary, but I'll leave it just in case.
//@param Direction True is Right, False is Left (see static const members RIGHT and LEFT)
private var nextFrameHide:Boolean = false;

override public function render():void
{
_tf.x=x+FlxG.scroll.x;
_tf.y=y+FlxG.scroll.y;
_tf.visible = true;
nextFrameHide = false;
}

private function onInputFieldRemoved(event:Event):void
{
//Clean up after ourselves
_tf.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
_tf.removeEventListener(Event.REMOVED, onInputFieldRemoved);
_tf.removeEventListener(Event.CHANGE, onTextChange);
}

private function onEnterFrame(event:Event):void
{
if(nextFrameHide)
_tf.visible=false;
nextFrameHide = true;
}

private function onTextChange(event:Event):void
{
if(forceUpperCase)
_tf.text = _tf.text.toUpperCase();

if(filterMode != NO_FILTER) {
var pattern:RegExp;
switch(filterMode) {
case ONLY_ALPHA: pattern = /[^a-zA-Z]*/g; break;
case ONLY_NUMERIC: pattern = /[^0-9]*/g; break;
case ONLY_ALPHANUMERIC: pattern = /[^a-zA-Z0-9]*/g; break;
case CUSTOM_FILTER: pattern = customFilterPattern; break;
default:
throw new Error("FlxInputText: Unknown filterMode ("+filterMode+")");
}
_tf.text = _tf.text.replace(pattern, "");
}
}

//@desc Text field background color
public function set backgroundColor(Color:uint):void { _tf.backgroundColor = Color; }
//@desc Text field border color
public function set borderColor(Color:uint):void { _tf.borderColor = Color; }
//@desc Shows/hides background
public function set backgroundVisible(Enabled:Boolean):void { _tf.background = Enabled; }
//@desc Shows/hides border
public function set borderVisible(Enabled:Boolean):void { _tf.border = Enabled; }

//@desc Text field background color
public function get backgroundColor():uint { return _tf.backgroundColor; }
//@desc Text field border color
public function get borderColor():uint { return _tf.borderColor; }
//@desc Shows/hides background
public function get backgroundVisible():Boolean { return _tf.background; }
//@desc Shows/hides border
public function get borderVisible():Boolean { return _tf.border; }

//@desc Set the maximum length for the field (e.g. "3" for Arcade type hi-score initials)
//@param Length The maximum length. 0 means unlimited.
public function setMaxLength(Length:uint):void
{
_tf.maxChars = Length;
}

//@desc Get the text the user has typed
public function getText():String
{
return _tf.text;
}

}

}

Is not necesary, change _tf to protected, now protected by default.

Thanks for this usefull class!
« Last Edit: Thu, Aug 12, 2010 by Tyranus » Logged

phmongeau
Jr. Member
**
Posts: 51



View Profile
« Reply #9 on: Thu, Aug 19, 2010 »

EDIT: Never mind, it works if I put it outside of the constructor.

Is there a way to make this work when the game is paused?

I need to have a text input in my games pause group.
If I add it to the PlayState, it works as expected, but if I add it to the pause group, it freezes at the preloader and throws an error. (something like "Cannot access a property or method of a null object reference. at FlxInputText()")

It would be great if someone could find how to do this, thanks.
« Last Edit: Fri, Aug 20, 2010 by phmongeau » Logged

Pages: [1]
  Print  
 
Jump to: