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?
).
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. 