Prob with Xith, Swing, textfield + listeners

Recently I played around with Xith and its Swing integration via UIWindow. As far as it concerns displaying everything is fine. But being a Swing user from version 0.5 back in 1998 I also wanted to add special keylisteners, focuslisteners and some other magic also.

It works - well kind of - but not as wanted.

Focus handling seems to be messed up :frowning: My textfield never gets focus - even if the keylistener is called it says : NO FOCUS! instead the GLCanvas always optains the focus … Mysteriously I can input data to the textfield and do see the blinking caret …
The only info I could track is “setting component to focus gained” which is printed to stdout and cannot be found in the xith sources. Where could this come from? JOGL maybe?

Keylisteners are also to be handled carefully. That’s the second prob. adding a keylistener to the GLCanvas (i.e. canvas.get3DPerr().getComponent() (and of course making use of that UIEventAdapter again) works fine for my own navigation in the scenery. But if you enter text to a textfield you would not await that when entering “Hello world” your avator would go forward when pressing the ''w" key that is - as you may guess by now - also being used for moving forwards.
Is there a special filter I can use? In Swing a listener added to a textfield would consume the event if not wanted for the outer plane but for GLCanvas it is different (even call event.consume() to stop any further event-handling for this special one). Sadly it cannot be workarounded by adding the UIEventAdapter for the textfields first and consuming the event …

Let me try to sum up the 3D event handling as far as I do understand it in the current context:

I can only add KeyListeners/MouseListeners to the GLCanvas. To make a Swing listener work I have to use UIEventAdapter that will transport this info to my textfield and also to any other UIEventAdapter that has been registered. There is no chance to consume that event if I am entering text into a textfield and do not want any other listener to be informed. Textfields will never gain a real focus though being editable - especially focus gained/lost events will never occur. Correct? Or am I missing something here?

Any info would be appreciated gracefully.

Not similar problem but related Swing problem I encountered was with texfield and any other entry fields not able to fire ActionListener when Enter key is pressed. As the checked UIWindowManager, the processEvent only process KeyEvent and dispatches it to focused text entry field. Can this be considered a bug?

My workaround was using InputMap, KeySroke and ActionMap on the textfield. Not a clean workaround but it worked i.e:
speedTxt.getInputMap().put(KeyStroke.getKeyStroke(’\n’), “action”);
//simulating ENTER

speedTxt.getActionMap().put(“action”, action);

Would appreciate if there’s any info on a better way to handle Swing events.

I was trying to figure out how to implement invisible JLayeredPane or GlassPane on xith3d canvas (unfortunately placing JFrame on canvas is out of the question, or is it?) so that I can place components on top of it and I can move them around.

Has anyone done it or considering doing it care to give some info?

Regarding the enter event the standard code would be quite similar to your solution by using


speedTxt.getKeyMap().addActionForKeyStroke(yourKeystroke, yourAction);

Adding a glasspane is a thing that is not supported as I do think by default. But if you can make use of JInternalFrame (it is a JComponent!) you could add a glasspane there. Sadly a JInternalFrame will not get focus - therefore you never could edit a textfield there because that one will not get focus either…

If you do not need textfields and so on but are only displaying labels and other components that do not contain interaction you also could use OverlayLayout. Sadly a textfield on a lower component would be drawn on top also - miracles of swing I believe.

Last chance to achieve something similar to glasspane is to render your normal panel with all your fields as a buffered image and replace the interactive panel by that image.

Forgot one alternative: If you just do need to disable all events - then add some code to your UIEventHandler to filter all unwanted input.

And the third answer after rereading your second post: :wink:

If you do want to have some overlay (graphics and so on) above your 3d world you may use

jcomponent.setOpaque(false)

which will disable background (i.e. you can see through it).

or use alpha colors for its background

jcomponent.setBackground(new Color(red,green,blue,alpha));

Do not forget to enable alpha blending for your UIWindow 8)

Thanks Stefan for your inputs. Guess I have to tweak some xith3d codes to make it happen.

Im running into the same problems you guys were having. I have a textfield that I want to put into a textarea when the user hits enter. Like a regular “chat” window.

However, Im running into the same problems:

  1. Enter doesnt seem to trigger the event, so it doesnt call my ActionListener.

  2. Keys and Mouse clicks to the panel doesnt “stop there” and it passes through to the game logic. Like the “w” analogy in the previous post. It also does this for my mouse clicks and picking.

Is this by design?

I was trying to use your workaround for the “enter issue” but I am a Swing newbie and I couldnt figure out what the heck you were doing (maybe its too late at night…)

can you please post a full example of your workaround and any other hints?

Thanks!!!

For 1: try one of the following solutions (as listed in the examples above):


TextField yourTextField = ...;
yourTextFielf.getInputMap().put(KeyStroke.getKeyStroke('\n'), "action");
// or: yourTextField.getKeyMap().addActionForKeyStroke(yourKeystroke, yourAction); 
yourTextField.getActionMap().put("action",  yourAction); 

By defaukt swing has the enter key registered but that seems to be removed in here - do not know why.
yourActon is just an instantiation of the action that is to be performed. you can get all registered actions for your textfield by using

 textField.getActions()

For 2: Still experimenting myself…

Thanks StefanD2, please let us know if you get #2 figured out!!!

Part 2 is not that easy … In fact there is something wrong with the handling of focus, key and mouse events when dealing with Swing. Or maybe I am doing something completly wrong …

If I have a textfield and add a mouse listener to my GLCanvas the textfield will get the event (Mouse_Click) but right after thet the GLCanvas will get it also!

A possible hack to workaround the doubled event problem would be to simulate some kind of focus handling yourself (did I mention that the AWT’s focus will always stay on the GLCanvas? :frowning: ).

The main idea for this quick hack lies upon getting to know when the user clicks on our textfield (this does not implement any focusHandling per TAB-Key - that has to be done somewhere else).

To give you an overview (only for keyboard input):

Init your Overlay (just an example here - you have to add an overlay and modify any required part)

UIWindowManager myUIWindowManager = new UIWindowManager(canvas);
myUIWindowManager.addOverlay(yourOverlay);
myUIWindowManager.setOverlayFocus(this);
myUIWindowManager.setPosition(this, 0, 0);
myUIWindowManager.setVisible(this, true);

Add a default UIEventListener (would not work without / but ensure having only one added in total):

final UIEventAdapter myUIEventAdapter = new UIEventAdapter(myUIWindowManager);
canvas.get3DPeer().getComponent().addMouseListener(myUIEventAdapter);
canvas.get3DPeer().getComponent().addKeyListener(myUIEventAdapter);

Take your class for handling keyboard input for moving around in your world (use your own - this one relies upon the one David contributed with his nice QuakeDemo) . Note that the instance of the class does not have to be registered anywhere anymore! We will add its caller lateron. This approach will allow you to switch back to your current code when there will be a fix for the current problem related to events connected to Swing.


final MovementKeyboardEventListener myMovementKeyboardEventListener = new MovementKeyboardEventListener(force);

Now we do need a special class to decide whether we have activated a JComponent (especially a textfield) or not. I used a KeyListener here but any other class would do also since this one does not listens for events by itself but simply decides whatever will be called due to the current event.
Sadly GLCanvas will get a ‘mouse clicked’ event right after the textfield got it => do need two indicators here.
I do not include those two booleans (isJComponent, isLastSourceAJcomponent). We do need these ones to get hold of the fact having JComponents we want to add special user action to. Just add them to your code where it fits.


final KeyListener myKeyListener = new KeyAdapter()
{
      public void keyTyped(KeyEvent e)
      {
            if (!isJComponent && !isLastSourceAJComponent)
                  myMovementKeyboardEventListener.eventDispatched(e);
      }

      public void keyPressed(KeyEvent e)
      {
            if (!isJComponent && !isLastSourceAJComponent)
                  myMovementKeyboardEventListener.eventDispatched(e);
      }

      public void keyReleased(KeyEvent e)
      {
            if (!isJComponent && !isLastSourceAJComponent)
                  myMovementKeyboardEventListener.eventDispatched(e);
      }
};

Right after that we build a AWTListener which will handle key and mouse events to glue everything:


AWTEventListener myAWTEventListener = new AWTEventListener()
{
      public void eventDispatched(AWTEvent event)
      {
            if (event instanceof KeyEvent)
            {
                  if ((event.getID() == KeyEvent.KEY_PRESSED))
                  {
                        myKeyListener.keyPressed((KeyEvent) event);
                  }
                  if ((event.getID() == KeyEvent.KEY_TYPED))
                  {
                        myKeyListener.keyTyped((KeyEvent) event);
                  }
                  if ((event.getID() == KeyEvent.KEY_RELEASED))
                  {
                        myKeyListener.keyReleased((KeyEvent) event);
                  }
            }
            else if (event instanceof MouseEvent)
            {
                  // to cope with the distorting order of event castings (Jcomponent, GLCanvas) store the last state ..
                  isLastSourceAJComponent = isJComponent;
                  // now get the real source
                  isJComponent = (event.getSource() instanceof JComponent);
            }
            else
            {
         myMovementKeyboardEventListener.eventDispatched(event);
            }
      }
};

Furthermore
To make it all work finally just enable event notification (key & mouse):

Toolkit.getDefaultToolkit().addAWTEventListener(
                        myAWTEventListener, AWTEvent.KEY_EVENT_MASK|AWTEvent.MOUSE_EVENT_MASK);

As this is simply a quick hack without changing anything in Xith or Jogl code I do really hope that there is a better way do do it. Nevertheless if it suits your requirements and you can live with its limit feel free to use it.

On the other hand if anyone finds a better solution anybody wanting to use Xith and Swing together would be deeply grateful - me, too. :wink:

3 months later and I’m finally getting around to this part of my game again.

It doesn’t look like anyone has come up with a better way to do this, so Im thinking I’ll have to use StefanD2’s workaround.

Anyone else able to overcome this? It sucks to have someone click a close button or one of my UIWindows to gain focus, and having it also affect my picking in the background.

Yuri, do you have any thoughts on this? Is the focus bugged? Are we doing something incorrectly?

Hi,

[quote]Yuri, do you have any thoughts on this? Is the focus bugged? Are we doing something incorrectly?
[/quote]
To be honest, UIWindow is not a part I am really familiar with. This was initially developed by David Yazel, so he should be a right person for questions on this topic (unfortunately, have not heard from him a long time).

From the other point, if somebody is familiar enough with Swing internals, we can dig into the code and figure out what is going on there. You can treat on me in all aspects of Xith3D related to scenegraph itself, state management, JOGL renderer, extensions support, picking etc.

Alternatively, you can wait until we come to use of Swing on our games (even I can not predict when we decide to do so)…

Yuri

Thanks for the quick response at least!

Maybe a page on Xith.org with active dev’s and thier roles on the project would be good. Unfortunately, I’m thinking it’s going to be a small list…