Lemur TextField notify when the user finish text input

Hello. I’m trying out Lemur and I wonder how can I add something like an ActionListener to a TextField so that it is called after the user finish input text with either ENTER or TAB? TextField supports Swing style actions?

In the code I see only getActionMap(), but that seems to be for setting the key actions and it’s already set with the standardActions.

But isn’t key actions what you want to set?

If you just want to know when they leave the field then you can also use a focus listener. Since you don’t want them to specifically do anything then ‘lost focus’ is probably the real event you want. That would cover cursoring out of the field, too (up down) as well as shift-tab, clicking somewhere else, etc…

That’s not the case for all applications, though, so Lemur does not make a judgment call on how you want to do it. (And since TextField supports single and multiline text, even enter and tab are not a given for ‘done’.)

To add a focus listener is something like:
myTextField.getControl(GuiControl.class).addFocusChangeListener(…your listener…)

http://jmonkeyengine-contributions.github.io/Lemur/javadoc/Lemur/com/simsilica/lemur/focus/FocusChangeListener.html

1 Like

Yes, the FocusChangeListener would help here. I just didn’t found anything like that because I expected addFocusChangeListener() to be in the class TextField and not in GuiControl.

Also, I would never guessed to use
myTextField.getControl(GuiControl.class).addFocusChangeListener(…your listener…)

I was trying to implement a text field that validates the user input and shows a red border if the input is not valid. i.e. Client-side form validation - Learn web development | MDN

FocusChangeListener is useless for that. Swing Action in the JTextField makes it very easy to implement in Swing.

Thank you for your help.

Validates it when? As they type?

…or when focus is lost?

If you want to validate it as they type then this might be helpful:

Games operate in a different environment than normal user applications and listeners are quite clumsy for a few reasons when you are already polling every frame anyway.

When focus is lost. :grinning:
Maybe add the code example for the FocusChangeListener somewhere? I really didn’t knew how to react when the user was finished input the text. Add this in the docu somewhere:
myTextField.getControl(GuiControl.class).addFocusChangeListener(…your listener…)

This is what confused me.

I don’t think I can agree to that. Java is still multi threaded environment and I try to poll stuff as little as possible in the rendering thread. All (as much as possible) CPU stuff I do outside of the rendering thread.

Yes, I wasn’t thinking.

1 Like

JME is calling update every frame… no matter what you do.

Listeners require lots of extra management. You have to add them then remember to remove them again, etc… and every change then gets a change event.

For forms, sometimes there is extra wiring so that the various validation listeners can see the rest of the form and interact with each other.

None of these things apply to the VersionedReference approach.

if( ref.update() ) {
    validateField();
}

ref.update() is a super cheap call (it’s just comparing two long values). There is already an outer update() method called every frame (generally forms are managed with an AppState unless you do things a harder way).

No listener leakage. No event garbage, etc…

I will look into VersionedReference. Thank you.

re: the above. Nah, I just need to add the add/removeFocusChangeListener() calls to Panel.java.

If it helps, philosophically, Lemur is a collection of “subsystems” for making a UI library. It just ALSO happens to provide some default implementations tying them together.

So Label, Button, etc. are just convenience wrappers around a JME Spatial with a GuiControl. They provide some convenient setup and some nice settable properties that are stylable, etc… but anyone can make their own GUI elements in a similar way, use the same styling, same GuiControl, and so on.

You can actually add a GuiControl to any JME spatial and it can participate in a UI, presuming you give it a good size and stuff.

The point is that when I’ve forgotten to add some convenience method to the Panel-related classes, there are always the underlying subsystems that can be tapped directly.

Edit: in case you haven’t seen it already, this is kind of an overview of the subsystems: Modules · jMonkeyEngine-Contributions/Lemur Wiki · GitHub

2 Likes

Hi. I need to come back to my original question. How to check if the user finished with the input of the text if there is no focus change? I have a button that should be enabled if the user finished with all needed text fields. But since the button is disabled there is no focus change.

I guess I need to add a custom

actionMap.put(new KeyAction(KeyInput.KEY_RETURN), FOCUS_NEXT);

But then I need to copy the FocusChange inner class because it’s private. Because I need the focus change behavior.

private static class FocusChange implements KeyActionListener {

Capture2

Why cant you just leave it active and test the user input upon submission?

And what do I do if the input is not valid? - I could show an annoying dialog. I could also do nothing and let the user wonder if my UI is broken.

Interesting. I guess on Windows it’s normal to leave the button active and just do nothing. In Linux and Eclipse the buttons are deactivated until user input is valid. I just tested it with some apps.

I like to leave the button deactivated until all input is entered. I think it’s much better for the user. I guess that’s subjective.

You could just not perform the form submission, but highlight the textfields instead.

button.addClickListener(l -> {
    boolean isValid = checkMyInput(...);
    if (isValid) {
        // submit my form
    } else {
        // show a validation error
    }
});

I don’t think you can solve this issue in a ‘one-solution-fits-all’. Now you assume the user is ready when he entered some input in the last input field. But what if he clicked on the tab button too soon, or he didn’t start entering values in the first field. The thing is, you don’t really have a proper trigger to know when the user is actualy ready with the form.

In your case I would just create a validation method and have it run every frame (or x times per second). When this method considers the form to be valid, enable your button.

No, you’d just call the original from your new action.

…but if enter will already try to change focus and you are validating on focus lost… then isn’t it already being validated now?

Yes, I do. On focus change I test if all needed values are entered.

Right, I can just save the old one and call it from my new one. I was hoping there is a better way to do it.

Yes, they are validated on focus lost. But the last TextField will not get a focus lost because the button is deactivated.

button.setEnabled(false)

Then I think something is wrong.

Disabled buttons should still be allowed to have focus… and even if not, “NEXT” should take you back to the top of the form.

Maybe there is a bug somewhere.