package org.flixel.ui;
import org.flixel.FlxG;
import org.flixel.ui.event.IFlxInputText;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import flash.events.Event;
import flash.events.IEventListener;
import flash.events.KeyboardEvent;
/*@formatter:off*/
/**
* Copyright (c) 2009 Martín Sebastián Wain
* License: Creative Commons Attribution 3.0 United States
* @link http://creativecommons.org/licenses/by/3.0/us/
*
* Input textfield extension for Flixel.
* Heavily modified to get it working on flixel-gdx.
* Supports multiline and filters.
* @link http://forums.flixel.org/index.php/topic,272.0.html
*
* @author Ka Wing Chin
* @author Gama11
* @author Mr_Walrus
* @author nitram_cero (Martin Sebastián Wain)
*//*@formatter:on*/
public class FlxInputText extends FlxUITouchable
{
private final String ImgTextAreaTopLeft = "org/flixel/data/pack:ninepatch_textarea_topleft";
private final String ImgTextAreaTopCenter = "org/flixel/data/pack:ninepatch_textarea_topcenter";
private final String ImgTextAreaTopRight = "org/flixel/data/pack:ninepatch_textarea_topright";
private final String ImgTextAreaMiddleLeft = "org/flixel/data/pack:ninepatch_textarea_middleleft";
private final String ImgTextAreaMiddleRight = "org/flixel/data/pack:ninepatch_textarea_middleright";
private final String ImgTextAreaBottomLeft = "org/flixel/data/pack:ninepatch_textarea_bottomleft";
private final String ImgTextAreaBottomCenter = "org/flixel/data/pack:ninepatch_textarea_bottomcenter";
private final String ImgTextAreaBottomRight = "org/flixel/data/pack:ninepatch_textarea_bottomright";
/**
* No filter
*/
public static final String NO_FILTER = "";
/**
* Only letters and no space.
*/
public static final String ONLY_ALPHA = "^\\d*$";
/**
* Only numbers and no space.
*/
public static final String ONLY_NUMERIC = "^\\d*$";
/**
* Only letters and numbers.
*/
public static final String ONLY_ALPHA_NUMERIC = "^[A-Za-z0-9_]+$";
/**
* Only letters, numbers and white space.
*/
public static final String ONLY_ALPHA_NUMERIC_SPACE = "^[A-Za-z0-9_ ]+$";
/**
* Custom filter.
*/
public static final String CUSTOM_FILTER = "";
/**
* All cases.
*/
public static final int ALL_CASES = 0;
/**
* Upper case.
*/
public static final int UPPER_CASE = 1;
/**
* Lower case.
*/
public static final int LOWER_CASE = 2;
/**
* Defines what text to filter. It can be NO_FILTER, ONLY_ALPHA,
* ONLY_NUMERIC, ONLY_ALPHA_NUMERIC or CUSTOM_FILTER.
*/
private String _filterMode = NO_FILTER;
/**
* This regular expression will filter out (remove) everything that matches.
* This is activated by setting filterMode = FlxInputText.CUSTOM_FILTER.
*/
public String customFilterPattern;
/**
* Text transform, ALL_CASES, LOWER_CASE and UPPER_CASE.
*/
private int _forceCase = ALL_CASES;
/**
* The textfield that will be displayed.
*/
public FlxTextExt textfield;
/**
* A function called when the enter key is pressed on this text box.
*/
public IFlxInputText callback;
/**
* The max amount of characters the textfield can contain.
*/
private int _maxLength = 0;
/**
* Internal, how many lines are allowed.
*/
private int _maxLines = 1;
/**
* Whether the textfield is in password mode or not.
*/
private boolean _passwordMode;
/**
* Tracks the amount of lines.
*/
private int _currentLineCounter = 1;
/**
* Buffer text.
*/
protected StringBuilder textBuffer;
/**
* Buffer password.
*/
private StringBuilder _passwordBuffer;
/**
* Tracks whether the text got changed or not.
*/
private boolean _isChanged;
/**
* Create a new <code>FlxInputText</code> object.
*
* @param X The x-position of the component.
* @param Y The y-position of the component.
* @param UISkin The skin that needs to be applied.
* @param Label The label along side the component.
* @param Width The width of the component. Default 0, unlimited width.
* @param Height The height of the component. Default 0, unlimited height.
*/
public FlxInputText(float X, float Y, FlxUISkin skin, String Label, int Width, int Height)
{
super(X, Y, skin, Label, Width, Height);
textfield = new FlxTextExt(X, Y - 6, Width, Height, null, true);
textfield.offset.y = (skin == null) ? -12 : 0;
textfield.setFormat(null, 16);
textBuffer = new StringBuilder();
_passwordBuffer = new StringBuilder();
FlxG.getStage().addEventListener(KeyboardEvent.KEY_TYPED, handleKeyDown);
}
/**
* Create a new <code>FlxInputText</code> object.
*
* @param X The x-position of the component.
* @param Y The y-position of the component.
* @param UISkin The skin that needs to be applied.
* @param Label The label along side the component
* @param Width The width of the component. Default 0, unlimited width.
*/
public FlxInputText(float X, float Y, FlxUISkin skin, String Label, int Width)
{
this(X, Y, skin, Label, Width, 0);
}
/**
* Create a new <code>FlxInputText</code> object.
*
* @param X The x-position of the component.
* @param Y The y-position of the component.
* @param UISkin The skin that needs to be applied.
* @param Label The label along side the component
*/
public FlxInputText(float X, float Y, FlxUISkin skin, String Label)
{
this(X, Y, skin, Label, 0, 0);
}
/**
* Create a new <code>FlxInputText</code> object.
*
* @param X The x-position of the component.
* @param Y The y-position of the component.
* @param UISkin The skin that needs to be applied.
*/
public FlxInputText(float X, float Y, FlxUISkin skin)
{
this(X, Y, skin, null, 0, 0);
}
/**
* Create a new <code>FlxInputText</code> object.
*
* @param X The x-position of the component.
* @param Y The y-position of the component.
*/
public FlxInputText(float X, float Y)
{
this(X, Y, null, null, 0, 0);
}
/**
* Create a new <code>FlxInputText</code> object.
*
* @param X The x-position of the component.
*/
public FlxInputText(float X)
{
this(X, 0, null, null, 0, 0);
}
/**
* Create a new <code>FlxInputText</code> object.
*/
public FlxInputText()
{
this(0, 0, null, null, 0, 0);
}
@Override
public void setDefaultSkin()
{
skin = new FlxUISkin();
skin.DISABLED = 1;
skin.HIGHLIGHT = 2;
skin.PRESSED = 2;
skin.ACTIVE_NORMAL = 2;
skin.ACTIVE_HIGHTLIGHT = 2;
skin.ACTIVE_PRESSED = 2;
skin.labelPosition = FlxUISkin.LABEL_TOP;
skin.labelOffset.y = -3;
skin.labelWidth = 200;
skin.setFormat(null, 8, 0x0099CC);
skin.setNinePatch(FlxNinePatch.TOP_LEFT, ImgTextAreaTopLeft, 4, 2);
skin.setNinePatch(FlxNinePatch.TOP_CENTER, ImgTextAreaTopCenter, 1, 2);
skin.setNinePatch(FlxNinePatch.TOP_RIGHT, ImgTextAreaTopRight, 4, 2);
skin.setNinePatch(FlxNinePatch.MIDDLE_LEFT, ImgTextAreaMiddleLeft, 4, 1);
skin.setNinePatch(FlxNinePatch.MIDDLE_RIGHT, ImgTextAreaMiddleRight, 4, 1);
skin.setNinePatch(FlxNinePatch.BOTTOM_LEFT, ImgTextAreaBottomLeft, 4, 2);
skin.setNinePatch(FlxNinePatch.BOTTOM_CENTER, ImgTextAreaBottomCenter, 1, 2);
skin.setNinePatch(FlxNinePatch.BOTTOM_RIGHT, ImgTextAreaBottomRight, 4, 2);
}
@Override
public void draw()
{
super.draw();
textfield.draw();
}
@Override
public void update()
{
checkFocus();
super.update();
}
/**
* Check whether a click has been occurred outside the textfield. Hides the
* keyboard on mobile.
*/
protected void checkFocus()
{
// Focus lost.
if(activated && FlxG.mouse.justPressed() && !overlapsPoint(_point.make(FlxG.mouse.x, FlxG.mouse.y)))
{
setActive(false);
// Remove the keyboard.
if(FlxG.mobile)
Gdx.input.setOnscreenKeyboardVisible(false);
}
}
/**
* This will automatically called when textfield got clicked. Allows to type
* in the textfield. Shows the keyboard on mobile.
*/
@Override
public void onChange()
{
setActive(true);
if(FlxG.mobile)
Gdx.input.setOnscreenKeyboardVisible(true);
}
/**
* Handles key presses generated on the stage.
*
* @param e The triggering keyboard event.
*/
IEventListener handleKeyDown = new IEventListener()
{
@Override
public void onEvent(Event e)
{
if(activated)
{
int key = ((KeyboardEvent) e).keyCode;
if(key == Keys.SHIFT_LEFT || key == Keys.SHIFT_RIGHT || key == Keys.CONTROL_LEFT || key == Keys.CONTROL_RIGHT || key == Keys.ALT_LEFT || key == Keys.ALT_RIGHT)
{
return;
}
// Backspace
if(key == Keys.BACKSPACE)
{
if(textBuffer.length() > 0)
{
textBuffer.delete(textBuffer.length() - 1, textBuffer.length());
if(_passwordMode)
_passwordBuffer.delete(_passwordBuffer.length() - 1, _passwordBuffer.length());
_isChanged = true;
}
}
// Enter
else if(key == Keys.ENTER)
{
if(callback != null)
callback.onEnter(textfield.getText());
if(1 < _maxLines && _currentLineCounter < _maxLines)
{
textBuffer.append("\n");
_isChanged = true;
}
}
// Add some text
else
{
if(textBuffer.length() < _maxLength || _maxLength == 0)
{
textBuffer.append(filter(String.valueOf(((KeyboardEvent) e).charCode)));
if(_passwordMode)
_passwordBuffer.append("*");
_isChanged = true;
}
}
if(_isChanged)
{
// Calculate whether it fits the bounding or not.
textfield.setText(textBuffer.toString());
if(_maxLines < textfield.getTotalLines())
textBuffer.deleteCharAt(textBuffer.length() - 1);
textfield.setText(_passwordMode ? _passwordBuffer.toString() : textBuffer.toString());
_currentLineCounter = textfield.getTotalLines() == -1 ? 1 : textfield.getTotalLines();
}
_isChanged = false;
}
}
};
@Override
public void destroy()
{
super.destroy();
FlxG.getStage().removeEventListener(KeyboardEvent.KEY_TYPED, handleKeyDown);
handleKeyDown = null;
textfield.destroy();
textfield = null;
callback = null;
textBuffer = null;
_passwordBuffer = null;
}
/**
* Checks an input string against the current filter and returns a filtered
* string.
*
* @param text Unfiltered text
* @return Text filtered by the the filter mode of the box
*/
protected String filter(String text)
{
if(_forceCase == UPPER_CASE)
text = text.toUpperCase();
else if(_forceCase == LOWER_CASE)
text = text.toLowerCase();
if(_filterMode != NO_FILTER)
{
if(!text.matches(_filterMode))
text = "";
}
return text;
}
/**
* Set the maximum lines the textfield is allowed.
*
* @param Lines
*/
public void setMaxLines(int Lines)
{
_maxLines = Lines;
}
/**
* Get the current case that got forced.
*
* @return
*/
public int getForceCase()
{
return _forceCase;
}
/**
* Enforce upper-case or lower-case
*
* @param Case The Case that's being enforced. Either ALL_CASES, UPPER_CASE
* or LOWER_CASE.
*/
public void setForceCase(int Case)
{
_forceCase = Case;
textfield.setText(filter(textfield.getText()));
}
/**
* Set the font size.
*
* @param Size
*/
public void setSize(float Size)
{
textfield.setSize(Size);
}
/**
* The maximum chars the textfield is allowed.
*
* @param Length
*/
public void setMaxLength(int Length)
{
_maxLength = Length;
if(textfield.getText().length() > _maxLength)
textfield.setText(textfield.getText().substring(0, _maxLength));
}
/**
* 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 int getMaxLength()
{
return _maxLength;
}
/**
* Make the textfield go in to password mode and show * for every char.
*
* @param enable
*/
public void setPasswordMode(boolean enable)
{
_passwordMode = enable;
}
/**
* Whether or not the textfield is a password textfield
*
* @param enable Whether to en- or disable password mode
*/
public boolean getPasswordMode()
{
return _passwordMode;
}
/**
* Set the filter mode that the text needs to be filtered. NO_FILTER,
* ONLY_ALPHA, ONLY_NUMERIC, ONLY_ALPHA_NUMERIC, ONLY_ALPHA_NUMERIC_SPACE or
* CUSTOM_FILTER
*
* @param filterMode
*/
public void setFilterMode(String filterMode)
{
_filterMode = filterMode;
textfield.setText(filter(textfield.getText()));
}
/**
* Defines what text to filter. It can be NO_FILTER, ONLY_ALPHA,
* ONLY_NUMERIC, ONLY_ALPHA_NUMERIC, ONLY_ALPHA_NUMERIC_SPACE or
* CUSTOM_FILTER (Remember to append "FlxInputText." as a prefix to those
* constants)
*/
public String getFilterMode()
{
return _filterMode;
}
/**
* Set text in the textfield.
*
* @param Text
*/
public void setText(CharSequence Text)
{
textfield.setText(Text.toString());
}
/**
* Get the text from the textfield.
*/
public CharSequence getText()
{
return textfield.getText();
}
}