JavaFX - JmeFXInputListener consuming Button release events

Hello everyone,

I am using following maven dependency for my project:

com.jayfella jme-jfx-11 1.1.4

Worked nice out of the box.
I have just slightly problems in certain situations

For example following:

-> 3D world is focused, and I press key W (for example) for moving Forward.
-> Now I click in a Java fx Panel, so that jfx container will grab the Focus.
-> Now I release key W.
-> The Event is consumed by JmeFXInputListener. (No further listeners from the InputManager will react)
-> In the General InputManager of the application the Button W is still listed as pressed.
-> So the move Action is still executed (Analog Listeners still react)… Even if Focus is again on 3D world and the button is not actually pressed.

Any idea how this can be solved?

Dirty Hack would be to call:
com.jme3.input.InputManager.reset()
from my project.
But that seems to be a ugly solution…

Thanks in advance for any help on this topic.

It’s my sons birthday today so I’m a little busy but I’ll put some thought into it.

There are certain situations where you’d want the focus to go straight back to JME and some where you wouldn’t, so the best approach would be to give the developer the choice. For example a boost button would want it straight back to JME. A button that brings up a panel wouldn’t.

I’ll think some more on it.

Hello jayfella,

Happy birthday to you’re son. :slight_smile:
Sounds like a good approach.
I guess it is a good idea, to give more flexibility to the developer
about focus decisions.
Thank you very much for putting effort into it.

1 Like

It would depend on what’s on the panel. If the panel just reveals more buttons, you’d still want JME processing key presses. I believe the way to think about this focus issue is as a component level issue, depending on the type of component.

TextInput, TextArea, TreeView, ListView (I’m sure there’s more) obviously when they get focus, those events shouldn’t travel to JME. Things like a button (inside a panel or not) shouldn’t ever block events JME would be interested in. (Unless you’re supporting accessibility in your game or something.)

Giving the dev the choice here (with sensible defaults) is obviously the safest approach, but how to implement that isn’t immediately obvious. Hopefully there’s another way, but my (quite possibly wrong) feeling is that a bytecode instrumentation approach may be needed to consume events on certain types of components. Which sounds like a lot of work (and messy), unless there’s a good place to intercept them somewhere inside JavaFX event processing.

Button navigation around UIs is quite common, though.

Generally, if your UI is separate from JME then it shouldn’t be gobbling the up events unless it also got the down event.

In applications sure, but is it common for games? Maybe I’m mistaken but I have a hard time thinking of a single game which allows that, and I tend to do some pretty UI-heavy stuff.

All of my games allow that.

If you are using the joystick and pop open the menu, you can navigate with the gamepad and select things. It’s functionality built into Lemur.

Alternatively, in some limited cases it might be possible to use a design like Elite Dangerous, which has a similar type of issue.

It’s awkward IMO, but IIRC it’s basically up to the user to focus/defocus the entire UI “panel” with a keypress to switch to a sort of “UI mode”. In this way I guess it’s even able to support keyboard navigation.

So basically you have to turn your game into vi. :slight_smile:

It’s really really simple.

If the UI gets an up event and did not get the down event then let JME have it.

Try to think of a use-case where that is bad and I will point out 100 that it works fine.

Edit: the only issue is if the integration between the two allows it… but it’s the right answer, otherwise.

Edit 2: the other side is true, also… if the UI received the down then it shouldn’t pass the up to JME… it will potentially confuse things reacting to down and up in specific ways.

1 Like

Well I’m sure you’ve put more thought into it than I have, so I won’t disagree with that concept. I’m less sure that it’ll be really really simple to implement, otherwise I have to wonder why it hasn’t been done yet, given that Swing and JFX have been used with JME for quite a few years.

I agree, if the UI has not received the Key Pressed event, it should not consume key released event.
I guess this can be changed in:
com.jayfella.jme.jfx.injme.input.JmeFXInputListener.onKeyEvent(KeyInputEvent)
This would solve my specific example which I described in my initial post.
However, there are also sometimes situations where you’d want the focus to go straight back to JME, when something on FX Panel is clicked. (This is independent of rejecting the release event)
So it would also be nice for the developer to have access to the FX Container, to trigger the lose of the UI focus whenever needed… But I am not sure if there is a better and cleaner approach to handle that whole topic.

Yeah there’s quite a bit of code in there now to cope with event management between JME and JFX. Most looks directed at converting JME events to FX events, but doing so in the other direction, I think is lacking? I’ll wait to see Jayfella’s thoughts before further speculating, maybe there’s an easy fix out there.

Trying to help improve on this if possible has been on my todo list some time, so if there’s some way I can help I’m listening. (Unfortunately my unreliable motivation level tends to vary drastically, almost none lately… :sweat_smile: ) It’s hard to know if a solution you spend time coming up with will be agreeable to the repo maintainers.

Ok. I think I’ve found a viable solution.

Thank you to @pspeed for your suggestion on how to handle the problem.

The code below monitors key pressed events from JME. If a key was pressed when JME had focus, and is released while JFX has focus, the event will bubble up to JME.

Test Case

There is a test class available in the repository to test this situation. The instructions are as follows to repeat the behavior.

  • With JME focused press the W key. The console will show pressed: true.
  • With your mouse click the javafx textfield.
  • Release the W key. The log will show pressed: false.

Other Additions

The above solution only partially solves the problem, so two additional methods have been added to hopefully solve the problem entirely in virtually all situations.

JavaFxUI.getInstance().grabFocus();
JavaFxUI.getInstance().loseFocus();

GrabFocus

The grabFocus() method will give JavaFx input focus.
This will be useful for situations where a GUI pops up in your game without user input. For example the player walks over and clicks a salesman - which in turn brings up a GUI. To force input to focus on that GUI you would call this method.

LoseFocus

The loseFocus() method will give JME input focus.
This can be used in cases where you may have a button that the user can press and resume their input focus instantly back to JME. For example if you have a “boost” button in a racer game.

These changes are not yet official so an official release has not yet been cut. I’m not completely convinced the CrossInputHandler.java code is bullet-proof. Any testing and suggestions are welcome. The code has been pushed to the github repository.

4 Likes

Thank you jayfella.
Have tested the changes for press/release handling, and it is looking very good.
I will do further intensive testings within the next days.

2 Likes

So good testing results so far? I’m still not sure this will solve the entire problem in every case, but I haven’t found the time to look into it at any meaningful level. :frowning:

One case I’m uncertain about is when you’ve clicked a JFX control which you wouldn’t want to hold focus, such as a button. Ex. click a JFX button, and then press a movement arrow key or whatever that generates events which JME would be interested in, but not the JFX control. This behavior would be control specific because if it were a TextInput or a TextField that was focused, you’d want JFX to process (and consume) your arrow key events rather than JME.

Typically, in a UI if you press a button then it gets locked as the target because if you move off before releasing then it cancels the press.

Users are so used to this stuff that they don’t even notice… until you mess it up and then they wonder why the UI feels broken but then can’t quite put their finger on why.

Edit: I realize after that there might be another way to read your response… but then I can’t make sense out of it since I would hope the JavaFX UIs are not consuming events that don’t get delivered to anything. But maybe it doesn’t know? (that would suck)

Yes I think we’re not connecting here. I need to do some tests of my own first and put together a better description of the problem, unless I find that I’m mistaken or there’s already a solution available.

By default if you click the gui, Jfx has focus. This is expected behaviour. If you don’t want that behaviour you can invoke the JavaFxGUI.getInstance().loseFocus() method in the click event to put the focus back onto jmonkey.

Inversely if a game action requires the user interacts with a gui - for example a merchant brings up a GUI you can .grabFocus() to give jfx focus.

1 Like

So just so I understand completely…

In an RPG where you use the mouse to select things on screen but otherwise use the keyboard to move your character around on screen… JFX would be ill-suited for this sort of UI unless you constantly switch focus back to the app on every button click?

As a user, I’d expect events not consumed by some control to be passed on… but maybe JavaFX doesn’t provide this (like Swing/AWT did).

Yes. It is working fine. I am using heavy FX UI in my simulation app, and I have not encountered any problems so far.

However, I have found out, that the same problem for mouse pressed/released existed.
So I created a pull request to solve the whole key/mouse-pressed(in JME)/released(in JFX) issue
a little bit different (based on the first solution draft).

For my use cases everthing is now fine.
I can navigate with (WASD) in JME world. Whenever I click on some
FX panel or FX Button, the JFX container will grab focus, and key buttons will not be handed over to JME. (Unless they are release events which needs to be passed to JME).
However, I can call JavaFxUI.getInstance().loseFocus(); if I want to get the focus right back to JME, so that keyboard navigation is working instantly again.

Just for clarification:
It all depends on the implementation of JmeFXInputListener, if Input events should be consumed, or handed over to JME.

1 Like