Here is an implementation I'm currently working on - sub classes from TextSprite. Has blinky cursor and text insertion on mouse select. No drag selection (if anyone wants to add that in - that would be great!). When you add the input text field to the stage, it auto focuses right now.
Give it a shot.
/////////////////////////////////////////////////////////////////
package;
import com.cmm.slport.text.InputTextSprite;
import flambe.display.FillSprite;
import flambe.display.Font;
import flambe.display.TextSprite;
import flambe.Entity;
import flambe.input.Key;
import flambe.input.KeyboardEvent;
import flambe.input.PointerEvent;
import flambe.math.Point;
import flambe.math.Rectangle;
import flambe.System;
import flambe.util.Signal1;
import flambe.util.SignalConnection;
import haxe.Timer;
class InputTextSprite extends TextSprite
{
static var _input:InputTextSprite;
public var characterLength:Int = cast Math.POSITIVE_INFINITY;
public var multiline:Bool = true;
private var _onInput:Signal1<KeyboardEvent> = new Signal1<KeyboardEvent>();
private var _keyConnex:SignalConnection;
private var _keyRepeater:Timer;
private var _cursor:FillSprite;
private var _insertionPoint:Int = 0;
@:keep public function new (font :Font, ?text :String = "")
{
super(font, text);
}
override public function onAdded()
{
super.onAdded();
if (_cursor == null)
{
_cursor = new BlinkCursor(0x000000, 1, _font.lineHeight);
_cursor.visible = false;
this.owner.addChild(new Entity().add(_cursor));
var hit = new FillSprite(0x000000, 100, _font.lineHeight);
hit.alpha._ = 0;
this.owner.addChild(new Entity().add(hit));
}
this.pointerEnabled = true;
this.pointerDown.connect(allowEdit);
startEdit();
}
private function allowEdit(e:PointerEvent)
{
e.stopPropagation();
if (_input != null)
_input.focusOut(e);
startEdit();
charAt(e);
}
private function startEdit()
{
_cursor.visible = true;
_input = this;
_keyConnex = System.keyboard.down.connect(editText);
System.pointer.up.connect(armfocusOut).once();
}
public function armfocusOut(e:PointerEvent)
{
System.pointer.down.connect(focusOut).once();
}
public function focus()
{
startEdit();
}
private function focusOut(e:PointerEvent)
{
_cursor.visible = false;
if(_keyConnex != null)
_keyConnex.dispose();
if (_keyRepeater != null)
_keyRepeater.stop();
}
private function editText(e:KeyboardEvent)
{
_onInput.emit(e);
if (_keyRepeater != null)
_keyRepeater.stop();
System.keyboard.up.connect(stopEdit).once();
if (e.key == Key.Delete || e.key == Key.Backspace)
{
deleteText();
}
else if(text.length <= characterLength)
{
var newChar:String = keyToString(e.key);
text = text.substring(0, _insertionPoint) + newChar + text.substring(_insertionPoint, text.length);
_insertionPoint++;
}
}
private function stopEdit(e:KeyboardEvent)
{
if (_keyRepeater != null)
_keyRepeater.stop();
}
private function deleteText()
{
if (_insertionPoint > 0)
{
text = text.substring(0, _insertionPoint - 1) + text.substring(_insertionPoint,text.length);
_insertionPoint--;
_keyRepeater = Timer.delay(deleteText, 100);
}
}
override function updateLayout()
{
super.updateLayout();
updateCursor();
}
@:access(flambe.display.TextLayout)
private function updateCursor()
{
var gy = 0.0;
var ii = 0;
var ll = _layout._glyphs.length;
var glyphs = _layout._glyphs;
var lineOffset = _layout._lineOffset;
var offsets = _layout._offsets;
var gx = 0.0;
if (_insertionPoint == 0)
{
setCursorAt(0, 0);
return;
}
while (ii < ll) {
var glyph = glyphs[ii];
if (glyph.charCode == "\n".code) {
gy += lineOffset;
} else {
gx = offsets[ii];
if (ii == _insertionPoint-1 || ii == glyphs.length - 1)
{
setCursorAt(gx + glyph.xAdvance, gy);
return;
}
}
ii++;
}
}
private function setCursorAt(x:Float, y:Float)
{
_cursor.x._ = x;
_cursor.y._ = y;
}
private function charAt(e:PointerEvent)
{
var point:Point = new Point(e.viewX - getViewMatrix().m02, e.viewY - getViewMatrix().m12);
var rect:Rectangle = new Rectangle();
var rectY:Float = 0;
var glyphs = _font.getGlyphs(_text);
var index = 0;
for (g in glyphs)
{
rect.width = g.xAdvance;
rect.height = _font.lineHeight;
rect.y = rectY;// + g.yOffset;
if (rect.contains(point.x, point.y))
{
_insertionPoint = index + 1;
updateCursor();
return;
}
rect.x += g.xAdvance;
if (rect.x >= wrapWidth._ || g == font.getGlyph("\n".code))
{
rect.x = 0;
rectY += _font.lineHeight;
}
index++;
}
_insertionPoint = index;
updateCursor();
}
private function keyToString(key:Key):String
{
switch(key)
{
case A: return (System.keyboard.isDown(Key.Shift))?"A":"a";
case B: return (System.keyboard.isDown(Key.Shift))?"B":"b";
case C: return (System.keyboard.isDown(Key.Shift))?"C":"c";
case D: return (System.keyboard.isDown(Key.Shift))?"D":"d";
case E: return (System.keyboard.isDown(Key.Shift))?"E":"e";
case F: return (System.keyboard.isDown(Key.Shift))?"F":"f";
case G: return (System.keyboard.isDown(Key.Shift))?"G":"g";
case H: return (System.keyboard.isDown(Key.Shift))?"H":"h";
case I: return (System.keyboard.isDown(Key.Shift))?"I":"i";
case J: return (System.keyboard.isDown(Key.Shift))?"J":"j";
case K: return (System.keyboard.isDown(Key.Shift))?"K":"k";
case L: return (System.keyboard.isDown(Key.Shift))?"L":"l";
case M: return (System.keyboard.isDown(Key.Shift))?"M":"m";
case N: return (System.keyboard.isDown(Key.Shift))?"N":"n";
case O: return (System.keyboard.isDown(Key.Shift))?"O":"o";
case P: return (System.keyboard.isDown(Key.Shift))?"P":"p";
case Q: return (System.keyboard.isDown(Key.Shift))?"Q":"q";
case R: return (System.keyboard.isDown(Key.Shift))?"R":"r";
case S: return (System.keyboard.isDown(Key.Shift))?"S":"s";
case T: return (System.keyboard.isDown(Key.Shift))?"T":"t";
case U: return (System.keyboard.isDown(Key.Shift))?"U":"u";
case V: return (System.keyboard.isDown(Key.Shift))?"V":"v";
case W: return (System.keyboard.isDown(Key.Shift))?"W":"w";
case X: return (System.keyboard.isDown(Key.Shift))?"X":"x";
case Y: return (System.keyboard.isDown(Key.Shift))?"Y":"y";
case Z: return (System.keyboard.isDown(Key.Shift))?"Z":"z";
case Number0: return "0";
case Number1: return "1";
case Number2: return "2";
case Number3: return "3";
case Number4: return "4";
case Number5: return "5";
case Number6: return "6";
case Number7: return "7";
case Number8: return "8";
case Number9: return "9";
case Numpad0: return "0";
case Numpad1: return "1";
case Numpad2: return "2";
case Numpad3: return "3";
case Numpad4: return "4";
case Numpad5: return "5";
case Numpad6: return "6";
case Numpad7: return "7";
case Numpad8: return "8";
case Numpad9: return "9";
case Period:return ".";
case Comma:return ",";
case Semicolon:return ";";
case Enter:return (multiline)?"\n":"";
case Quote:return "\"";
case Space:return " ";
default: return "";
}
return "";
}
private function get_onInput():Signal1<KeyboardEvent>
{
return _onInput;
}
public var onInput(get_onInput, null):Signal1<KeyboardEvent>;
}
////////////////////////////////////////////////////