Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FlxInputText on Android: No soft keyboard #70

Closed
Shalmezad opened this issue Jul 25, 2014 · 23 comments · Fixed by #257
Closed

FlxInputText on Android: No soft keyboard #70

Shalmezad opened this issue Jul 25, 2014 · 23 comments · Fixed by #257

Comments

@Shalmezad
Copy link
Contributor

It was already mentioned on this issue: #41
But wanted to make a separate issue.

Basically, when tapping on a FlxInputText, the carrot appears, but the keyboard doesn't come up.
I haven't tested with a hard keyboard yet, since all of my devices are soft-keyboard.

Looking at possible solutions now. It appears OpenFL has the capability to access the Java Native Interface. So may need to do something like this (within a #if android of course):

//When created:
        var JNIShowSoftKeyboard:Dynamic = JNI.createStaticMethod("flixel/addons/ui/KeyboardHandler", "showSoftKeyboard");
        var JNIHideSoftKeyboard:Dynamic = JNI.createStaticMethod("flixel/addons/ui/KeyboardHandler", "hideSoftKeyboard");
//When hasFocus:
        JNIShowSoftKeyboard();
//When !hasFocus:
        JNIHideSoftKeyboard();

Of course, will have to look at how Haxe handles native code. Especially since the normal way for handling the soft keyboard is:

InputMethodManager _imm = (InputMethodManager)SOME_CONTEXT.getSystemService(Service.INPUT_METHOD_SERVICE);
//then:
_imm.toggleSoftInput(InputMethodManager.SHOW_FORCED,0);
//or:
_imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY,0);

Which requires the current application context.

@Shalmezad
Copy link
Contributor Author

Looks like we might be in luck.
After some digging through, I found this:
https://github.com/openfl/openfl-native/blob/master/flash/display/InteractiveObject.hx

Might be able to do something like:

//At top, in a #if android:
         private static var lime_display_object_request_soft_keyboard = Lib.load("lime", "lime_display_object_request_soft_keyboard",1);
//in set_hasFocus()
        #if android
          lime_display_object_request_soft_keyboard(this);
        #end

@Gama11
Copy link
Member

Gama11 commented Jul 25, 2014

Shouldn't that be #if mobile? Or does it only work for android?

@Shalmezad
Copy link
Contributor Author

I assume the lime based methods would be both iOS and android. I have xcode on my mac, so I can check on this when I get it working.
Still trying to get the lime call to work within flixel-ui. Looking at the tests, it should be as simple as:

     sprite.requestSoftKeyboard();

But, that's with an openfl sprite. The sprites I've seen in flixel and flixel-ui so far have been flash sprites. Could create a new Sprite, but seems like a waste.

@samsieber
Copy link

Has there been any progress on this? This is a deal breaker for me, but I'd really like to use flixel.

@larsiusprime
Copy link
Member

Not so far, but if this is a big issue for people I can try to push for it. I don't have an android device myself so someone else would have to add the missing feature, but I can help push for it to be more of a priority.

@Gama11
Copy link
Member

Gama11 commented Sep 23, 2014

You could just use an OpenFL input TextField, if you don't want to display anything on top of it.

@larsiusprime
Copy link
Member

Would love to close this if someone on android can help me resolve it. (I have no android device)

@Shalmezad
Copy link
Contributor Author

I can help test as needed (got a couple devices that I know I can run on), but unfortunately don't have the time to go through and find the fix at the moment (unless you're willing to wait about 5 weeks).

@Tiago-Ling
Copy link
Contributor

Hi, just pinging this issue to see if anybody got a flixel-based solution to this question or even a workaround for this to work as it should on mobile?

I can help test any solution / workaround in Android 4.4.4, 5.0.1 and iOS 7.

@larsiusprime
Copy link
Member

One thing that could help is if someone could describe what they expect to happen on Android, vs what actually happens, vs what happens with regular OpenFL Input text fields. Since I don't have an android phone I'm totally in the dark here, but maybe it's a simple fix if I just knew what the standard behavior was.

@Tiago-Ling
Copy link
Contributor

I'm doing this right now - will return here with feedback later today. What i need is the traditional soft-keyboard stuff:

  • When the user clicks on the textfield the soft keyboard appears;
  • When it appears, the screen contents move to keep the textfield on the screen if needed;
  • When the textfields loses focus the soft keyboard should also be dismissed;
  • Optionally the user can set the "return" key to dismiss the keyboard automatically.

@Tiago-Ling
Copy link
Contributor

Small update regarding Android:

  • OpenFL 3.0.3 legacy has support for soft keyboard out of the box;
  • I'm using the following code to show allow editing of a TextField:
        var textfield = new TextField();
        textfield.x = 540;
        textfield.y = 440;
        textfield.type = TextFieldType.INPUT;
        textfield.textColor = 0x000000;
        textfield.border = true;
        textfield.borderColor = 0xFFFF00;
        textfield.background = true;
        textfield.backgroundColor = 0xFFFFFF;
        textfield.width = 200;
        textfield.height = 40;
        textfield.setTextFormat(new TextFormat(null, 32));

        //Mobile stuff
        #if (android || ios)
        textfield.needsSoftKeyboard = true;
                //This should work in "next" i think, but causes a compiler error legacy
        //textfield.softKeyboardInputAreaOfInterest = new Rectangle(540, 440, 200, 40);
                //This does not have any effect afaik (available on legacy only)
        textfield.moveForSoftKeyboard = true;
        #end

        FlxG.addChildBelowMouse(textfield);
  • The code above does not handle cases in which the soft keyboard appears over the input TextField with focus. So any text with its y position higher than a certain point will be hidden by the soft keyboard;
  • One small thing to note: as far as i have tested, the blinking caret is always black so it won't appear if the TextField background color is also black.

I'll keep testing to see if i can make things move when requesting the keyboard.

@larsiusprime
Copy link
Member

Okay cool, so say in a normal OpenFL app, what happens? Does the entire openfl stage get moved upwards? How does it know how to move things?

We could solve this by moving the entire FlxGame (itself a Sprite on the OpenFL displaylist) up perhaps?

@Tiago-Ling
Copy link
Contributor

In a normal OpenFL application, created using OpenFL 3.0.3, Lime 2.3.3 and Hxcpp 3.2.37 with the following code: http://pastebin.com/Vpd6tgUk

  • Next: TextField input does work in Flash and HTML5, but doesn't in Windows, Neko and Android (probably not working in all native targets). Problems specific to Android:
    • The softKeyboardInputAreaOdInterest does not work;
    • Pressing the back button does not close the application as it did in legacy;
    • The position of the TextField is different than in legacy, as if the stage size is shrunk;
  • Legacy: The TextField input works as expected, but unfortunately no movement of the application in relation to the soft keyboard.

Looks like there's still some work to be done for Android / mobile in next. I suppose we can manually move the FlxGame in legacy when showing the soft keyboard. However i did not find any related event in OpenFL for us to listen to (something like SoftKeyboardEvent.SHOW). There is this old repository here in which the guy uses native java code to do this.

@larsiusprime
Copy link
Member

Cool, well let's check in with OpenFL and see what their plans are; open an issue with them if there's isn't one already (if there is, comment on it and link here?), once they have things sorted out, we can come up with a next-compatible solution.

I figure we can implement our own solution when the soft keyboard pops up, let's ping OpenFL about what events might be available to us. Worst case scenario we can just test whether the soft keyboard ALWAYS appears when an input textfield gets touched, and just drive the behaviour off of the "textfield clicked/touched" event or whatever, even though that's a bit sloppier.

@Tiago-Ling
Copy link
Contributor

We can check in FOCUS_IN if the target is a textfield and have the property needsSoftKeyboard set to true - if so we must get the soft keyboard size (changes according to device, resolution, dpi, etc) and move the screen to accommodate it.

The first part which is listening to the focus event and checking if the textfield requires the soft keyboard is easy. Getting the actual size of the keyboard might require some JNI code. This question gives us more insight into the question: http://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android

Here is a sample which does work in Android (OpenFL legacy), the only thing missing is the exact amount to move the screen:
http://pastebin.com/Q2WbwtvP

About issues in OpenFL, the ones most related to this are:

Do you think we need to open a separate issue for this one?

@larsiusprime
Copy link
Member

I'm actually working with Joshua on openfl text fields right now, so maybe we'll finally have a solution for this.

@Shalmezad
Copy link
Contributor Author

If you need any help testing it on android, let me know.

@srel90
Copy link

srel90 commented May 8, 2016

Has anyone know this issue progress?

@Tiago-Ling
Copy link
Contributor

I had the same problem in my last project and as a workaround i managed to get the soft keyboard working by using an OpenFL TextField:

input = new openfl.text.TextField();
input.x = distance.x + (distance.width / 4);
input.y = 285;
input.type = openfl.text.TextFieldType.INPUT;
input.textColor = 0x000000;
input.border = true;
input.borderColor = 0xFFFF00;
input.background = true;
input.backgroundColor = 0xFFFFFF;
input.width = Std.int(distance.width / 2);
input.height = 38;
input.defaultTextFormat = new openfl.text.TextFormat(null, 32);
input.needsSoftKeyboard = true;
input.maxChars = 16;
input.moveForSoftKeyboard = true;
input.addEventListener(openfl.events.KeyboardEvent.KEY_DOWN, onMobileEnter);

FlxG.addChildBelowMouse(input);
FlxG.stage.focus = input;

onMobileEnter was just a function to save the data of the text field and dismiss the keyboard (non-relevant parts omitted):

function onMobileEnter(e:openfl.events.KeyboardEvent) {
    if (e.keyCode == 13) {
        remove(input);
        input.__dismissSoftKeyboard();
        input.removeEventListener(openfl.events.KeyboardEvent.KEY_DOWN, onMobileEnter);
        FlxG.removeChild(input);
    }
}

Hope this helps!

@Gioele-Bencivenga
Copy link

Is there any way to help with this issue?
I have a flixel project using haxeui for the interface and would prefer not having to use OpenFL TextFields.

I have 2 android devices to test on in case it's needed.

@Jorl17
Copy link

Jorl17 commented Feb 11, 2022

I'm not sure if this is still relevant, but I fixed this issue by grabbing what @Tiago-Ling created, and I have been using my solution on both iOS and Android to great success. I created an FlxInputText wrapper (GFTextInput) that just defaults to FlxInputText on non-mobile, and, on mobile, uses an OpenFL textfield "under the hood" to fill-in an FlxInputText. So what we have is:

  • FlxInputText visuals
  • FlxInputText semantics for several things
  • OpenFL TextField "under-the-hood"
  • A "ClickArea" is placed over the FlxInputText to "trigger" the keyboard by focusing the "hidden" OpenFL TextField
  • When the field is focused, we also clear it (you can change this, obviously)
  • When enter is pressed, we remove focus from the element, thus sending the keyboard away
  • On every keypress, and on every update, we manually synchronize the state of the OpenFL field and FlxInputText (we copy the OpenFL text to FlxInputText)
  • We take additional precautions to support maxLength (still need to be improved, but good enough for my use case)

Here is my implementation:

package utils.ui;
import flixel.FlxG;
import flixel.group.FlxGroup;
import flixel.addons.ui.FlxInputText;
import flixel.util.FlxColor;

/**
* This is so hackish I might just marry it.
*
* On desktop/html5, this acts as (and IS) a regular input. I suspect that html5 should go the mobile route if it's
* running on an actual mobile device, but I'll check that later
*
* Anyway, on mobile, we use a set of devious hacks to get a virtual keyboard to popup on screen. That keyboard inputs
* to a hidden OpenFL field and we constantly copy the content of that field to our real/visible FlxInputText. We then
* mimick some properties of FlxInputText using getters and setters. We use a ClickArea to set focus on the hidden
* OpenFL field, and we also listen to enter keypresses to dismiss the keyboard by removing focus from the field. We
* also need special care to manually deal with maxLength, because the FlxText setter does not honour MaxLength on its
* own.
*
* Overall it's a very hacky solution on mobile, but it works!
**/
#if !mobile
typedef GFTextInput = FlxInputText;
#else
class GFTextInput extends FlxGroup {
    public final inputText : FlxInputText;
    private final _flTextField : openfl.text.TextField;
    private final clickArea : ClickArea;
    public var x(default,set) : Float;
    public var y(default,set) : Float;
    public var maxLength(default,set) : Int;
    public var text(get,set) : String;
    public function new(X:Float = 0, Y:Float = 0, Width:Int = 150, ?Text:String, size:Int = 8, TextColor:Int = FlxColor.BLACK,
                        BackgroundColor:Int = FlxColor.WHITE, EmbeddedFont:Bool = true) {
        super();
        x = X;
        y = Y;
        inputText = new FlxInputText(x,y,Width,Text,size,TextColor,BackgroundColor,EmbeddedFont);
        _flTextField = new openfl.text.TextField();
        _flTextField.needsSoftKeyboard = true;
        _flTextField.text = text;
        _flTextField.x = x;
        _flTextField.y = y;
        _flTextField.type = openfl.text.TextFieldType.INPUT;
        _flTextField.addEventListener(openfl.events.KeyboardEvent.KEY_DOWN, function (e:openfl.events.KeyboardEvent) {
            if (inputText.maxLength == 0 || _flTextField.text.length <= inputText.maxLength)
                inputText.text = _flTextField.text;
            if (e.keyCode == 13)
                FlxG.stage.focus = null;

        });
        clickArea = new ClickArea(x,y,Width,inputText.height, function () {
            text = '';
            FlxG.stage.focus = _flTextField;
        });
        add(inputText);
        add(clickArea);
        FlxG.addChildBelowMouse(_flTextField);
    }

    public override function update(elapsed) {
        if (inputText.maxLength == 0 || _flTextField.text.length <= inputText.maxLength)
            inputText.text = _flTextField.text;
        super.update(elapsed);
    }

    public function set_x(v:Float) {
        if (inputText != null)
            inputText.x = v;

        if (clickArea != null)
            clickArea.x =v;

        return x = v;
    }


    public function set_y(v:Float) {
        if (inputText != null)
            inputText.y = v;

        if (clickArea != null)
            clickArea.y = v;

        return y = v;
    }

    function set_text(value:String):String {
        inputText.text = value;
        _flTextField.text = value;

        return value;
    }

    function get_text(): String {
        return inputText.text;
    }

    function set_maxLength(v : Int): Int {
        return maxLength = inputText.maxLength = v;
    }

    public override function destroy() {
        FlxG.removeChild(_flTextField);
    }
}
#end

Also note that ClickArea is a class I implemented as below:

import flixel.addons.ui.FlxUIButton;
/**
* A clickarea. Basically an invisible button to put over things.
**/
class ClickArea extends FlxUIButton {
    public var onClick : Void->Void;
    public function new(x: Float, y: Float, width: Float, height: Float, onClick: Void->Void = null) {
        super(x, y, null, false, true);
        this.width = width;
        this.height = height;
        this.onClick = onClick;
        immovable = true;
        scrollFactor.set();
        onUp.callback = function () { if (this.onClick != null) this.onClick(); };
    }
}

@Geokureli
Copy link
Member

@Jorl17 This is cool, IMO FlxInputText is pretty garbage, and instead it should be doing something like this to leverage a better input system to begin with. Those would be a ton of manual testing involved in this, but I think it would be really cool to rebuild FlxInputText from the ground up using openfl TextFields to get keyboard text input.

Tbh, I've been thinking about the benefits of this for a few weeks, and I hadn't even considered mobile keyboards yet

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants