diff --git a/assets/images/editors/ui/inputbox.png b/assets/images/editors/ui/inputbox.png index a81994a5..229df739 100644 Binary files a/assets/images/editors/ui/inputbox.png and b/assets/images/editors/ui/inputbox.png differ diff --git a/source/funkin/editors/UIDebugState.hx b/source/funkin/editors/UIDebugState.hx index a14248a3..1320289d 100644 --- a/source/funkin/editors/UIDebugState.hx +++ b/source/funkin/editors/UIDebugState.hx @@ -99,6 +99,7 @@ class UIDebugState extends UIState { } ])); }, 130, 48)); + add(new UITextBox(10, 220, "")); } public override function update(elapsed:Float) { diff --git a/source/funkin/editors/ui/IUIFocusable.hx b/source/funkin/editors/ui/IUIFocusable.hx new file mode 100644 index 00000000..2f67c6af --- /dev/null +++ b/source/funkin/editors/ui/IUIFocusable.hx @@ -0,0 +1,10 @@ +package funkin.editors.ui; + +import lime.ui.KeyModifier; +import lime.ui.KeyCode; + +interface IUIFocusable { + public function onKeyDown(e:KeyCode, modifier:KeyModifier):Void; + public function onKeyUp(e:KeyCode, modifier:KeyModifier):Void; + public function onTextInput(text:String):Void; +} \ No newline at end of file diff --git a/source/funkin/editors/ui/UISprite.hx b/source/funkin/editors/ui/UISprite.hx index 9d95dd71..291aae31 100644 --- a/source/funkin/editors/ui/UISprite.hx +++ b/source/funkin/editors/ui/UISprite.hx @@ -21,6 +21,22 @@ class UISprite extends FlxSprite { public var cursor:MouseCursor = ARROW; + public var focused(get, set):Bool; + + private inline function get_focused():Bool + return UIState.state.currentFocus == cast this; + + private inline function set_focused(b:Bool):Bool { + if (get_focused()) { + if (!b) + UIState.state.currentFocus = null; + } else { + if (b) + UIState.state.currentFocus = cast this; + } + return b; + } + public override function update(elapsed:Float) { hovered = false; pressed = false; diff --git a/source/funkin/editors/ui/UIState.hx b/source/funkin/editors/ui/UIState.hx index e0eebfc2..5c70edb5 100644 --- a/source/funkin/editors/ui/UIState.hx +++ b/source/funkin/editors/ui/UIState.hx @@ -1,5 +1,8 @@ package funkin.editors.ui; +import lime.ui.KeyModifier; +import lime.ui.KeyCode; +import openfl.events.KeyboardEvent; import funkin.system.framerate.Framerate; import funkin.editors.ui.UIContextMenu.UIContextMenuCallback; import openfl.ui.Mouse; @@ -15,6 +18,7 @@ class UIState extends MusicBeatState { public var buttonHandler:Void->Void = null; public var hoveredSprite:UISprite = null; + public var currentFocus:IUIFocusable = null; private var __rect:FlxRect; private var __mousePos:FlxPoint; @@ -27,6 +31,25 @@ class UIState extends MusicBeatState { __mousePos = FlxPoint.get(); super.create(); Framerate.offset.y = 30; + + FlxG.stage.window.onKeyDown.add(onKeyDown); + FlxG.stage.window.onKeyUp.add(onKeyUp); + FlxG.stage.window.onTextInput.add(onTextInput); + } + + private function onKeyDown(e:KeyCode, modifier:KeyModifier) { + if (currentFocus != null) + currentFocus.onKeyDown(e, modifier); + } + + private function onKeyUp(e:KeyCode, modifier:KeyModifier) { + if (currentFocus != null) + currentFocus.onKeyUp(e, modifier); + } + + private function onTextInput(str:String) { + if (currentFocus != null) + currentFocus.onTextInput(str); } public function updateButtonHandler(spr:UISprite, buttonHandler:Void->Void) { @@ -69,6 +92,9 @@ class UIState extends MusicBeatState { buttonHandler = null; } + if (FlxG.mouse.justReleased) + currentFocus = (hoveredSprite is IUIFocusable) ? (cast hoveredSprite) : null; + if (hoveredSprite != null) { Mouse.cursor = hoveredSprite.cursor; hoveredSprite = null; @@ -80,6 +106,9 @@ class UIState extends MusicBeatState { public override function destroy() { super.destroy(); __mousePos.put(); + FlxG.stage.window.onKeyDown.remove(onKeyDown); + FlxG.stage.window.onKeyUp.remove(onKeyUp); + FlxG.stage.window.onTextInput.remove(onTextInput); } public function openContextMenu(options:Array, ?callback:UIContextMenuCallback, ?x:Float, ?y:Float) { diff --git a/source/funkin/editors/ui/UITextBox.hx b/source/funkin/editors/ui/UITextBox.hx new file mode 100644 index 00000000..887ef063 --- /dev/null +++ b/source/funkin/editors/ui/UITextBox.hx @@ -0,0 +1,110 @@ +package funkin.editors.ui; + +import flixel.math.FlxPoint; +import lime.ui.KeyModifier; +import lime.ui.KeyCode; +import openfl.desktop.Clipboard; +import openfl.geom.Rectangle; + +class UITextBox extends UISliceSprite implements IUIFocusable { + public var label:UIText; + + public var position:Int = 0; + public var multiline:Bool = false; + public var caretSpr:FlxSprite; + + public function new(x:Float, y:Float, text:String = "", width:Int = 320, height:Int = 32, multiline:Bool = false) { + super(x, y, width, height, 'editors/ui/inputbox'); + + label = new UIText(0, 0, width, text); + members.push(label); + + caretSpr = new FlxSprite(0, 0); + caretSpr.makeGraphic(1, 1, -1); + caretSpr.scale.set(1, label.size); + caretSpr.updateHitbox(); + members.push(caretSpr); + this.multiline = multiline; + } + + var cacheRect:Rectangle = new Rectangle(); + + public override function update(elapsed:Float) { + super.update(elapsed); + var off = multiline ? 4 : ((bHeight - label.height) / 2); + label.follow(this, 4, off); + + if (hovered && FlxG.mouse.justReleased) { + // get caret pos + var pos = FlxG.mouse.getScreenPosition(camera, FlxPoint.get()); + pos.x -= label.x; + pos.y -= label.y; + + var index = label.textField.getCharIndexAtPoint(pos.x, pos.y); + if (index > -1) + position = index; + + pos.put(); + } + framesOffset = (focused ? 18 : (hovered ? 9 : 0)); + @:privateAccess { + if (focused) { + caretSpr.alpha = (FlxG.game.ticks % 666) >= 333 ? 1 : 0; + + var curPos = switch(position) { + case 0: + FlxPoint.get(0, 0); + default: + if (position >= label.text.length) { + label.textField.__getCharBoundaries(label.text.length-1, cacheRect); + FlxPoint.get(cacheRect.x + cacheRect.width, cacheRect.y); + } else { + label.textField.__getCharBoundaries(position, cacheRect); + FlxPoint.get(cacheRect.x, cacheRect.y); + } + }; + caretSpr.follow(this, 4 + curPos.x, off + curPos.y); + curPos.put(); + } else { + caretSpr.alpha = 0; + } + } + } + + public function onKeyDown(e:KeyCode, modifier:KeyModifier) { + switch(e) { + case LEFT: + changeSelection(-1); + case RIGHT: + changeSelection(1); + case BACKSPACE: + if (position > 0) { + label.text = label.text.substr(0, position-1) + label.text.substr(position); + changeSelection(-1); + } + case HOME: + position = 0; + case END: + position = label.text.length; + case V: + if (modifier == KeyModifier.LEFT_CTRL || modifier == KeyModifier.RIGHT_CTRL ) { + // paste + var data:String = Clipboard.generalClipboard.getData(TEXT_FORMAT); + if (data != null) + onTextInput(data); + } + default: + // nothing + } + } + + public function changeSelection(change:Int) { + position = FlxMath.wrap(position + change, 0, label.text.length); + } + public function onKeyUp(e:KeyCode, modifier:KeyModifier) {} + + public function onTextInput(text:String):Void { + label.text = label.text.substr(0, position) + text + label.text.substr(position); + position += text.length; + } +} \ No newline at end of file diff --git a/source/funkin/scripting/Script.hx b/source/funkin/scripting/Script.hx index b25aa4ca..ff06a8a3 100644 --- a/source/funkin/scripting/Script.hx +++ b/source/funkin/scripting/Script.hx @@ -31,7 +31,7 @@ class Script extends FlxBasic implements IFlxDestroyable { // Flixel related stuff "FlxG" => flixel.FlxG, - "FlxSprite" => flixel.FlxSprite, + "FlxSprite" => funkin.system.FunkinSprite, "FlxBasic" => flixel.FlxBasic, "FlxCamera" => flixel.FlxCamera, "state" => flixel.FlxG.state,