Hi guys,
I have, up to this point, always heard that using Swing with LWJGL was not a good idea because lightweight and heavyweight components dont mix well. However, I have recently read the following tutorial and found out that this is now supported by the Swing library.
Source: http://www.oracle.com/technetwork/articles/java/mixing-components-433992.html
I quickly tested it with a small test application and it really worked!
Even Swing popup menus were correctly displayed on top of the lwjgl canvas.
Now, I am using windows 7 64bit, and I would like to know if this works on all operating systems. I would be glad if others could test this and tell me their results.
Here is the code I used for my test:
public class SwingFrame_LWJGL {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
SwingFrame_LWJGL window = new SwingFrame_LWJGL();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
private volatile float[] vertices;
private JFrame frame;
private Canvas canvas;
private Thread gameThread;
private boolean running;
private volatile boolean needValidation;
private volatile boolean needUpdateViewport;
public SwingFrame_LWJGL() {
frame = new JFrame();
frame.addWindowListener(new WindowListener() {
public void windowOpened(WindowEvent arg0) {
}
public void windowIconified(WindowEvent arg0) {
}
public void windowDeiconified(WindowEvent arg0) {
}
public void windowDeactivated(WindowEvent arg0) {
}
public void windowClosing(WindowEvent arg0) {
}
public void windowClosed(WindowEvent arg0) {
terminate();
}
public void windowActivated(WindowEvent arg0) {
}
});
frame.setTitle("Swing + LWJGL");
frame.setBounds(100, 100, 1024, 768);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JMenuBar menuBar = new JMenuBar();
frame.setJMenuBar(menuBar);
needValidation = true;
MenuListener menuListener = new MenuListener() {
public void menuSelected(MenuEvent e) {
if (needValidation) {
needValidation = false;
frame.validate();
System.out.println("Validate");
}
}
public void menuDeselected(MenuEvent e) {
}
public void menuCanceled(MenuEvent e) {
}
};
JMenu mnNewMenu = new JMenu("New menu");
mnNewMenu.addMenuListener(menuListener);
menuBar.add(mnNewMenu);
for (int i = 0; i < 4; i++) {
JMenuItem menuItem = new JMenuItem("New menu item");
mnNewMenu.add(menuItem);
}
JMenu mnNewMenu_1 = new JMenu("New menu");
mnNewMenu_1.addMenuListener(menuListener);
menuBar.add(mnNewMenu_1);
for (int i = 0; i < 4; i++) {
JMenuItem menuItem = new JMenuItem("New menu item");
mnNewMenu_1.add(menuItem);
}
JMenu mnNewMenu_2 = new JMenu("New menu");
mnNewMenu_2.addMenuListener(menuListener);
menuBar.add(mnNewMenu_2);
for (int i = 0; i < 4; i++) {
JMenuItem menuItem = new JMenuItem("New menu item");
mnNewMenu_2.add(menuItem);
}
JMenu mnNewMenu_3 = new JMenu("New menu");
mnNewMenu_3.addMenuListener(menuListener);
menuBar.add(mnNewMenu_3);
for (int i = 0; i < 4; i++) {
JMenuItem menuItem = new JMenuItem("New menu item");
mnNewMenu_3.add(menuItem);
}
frame.getContentPane().setLayout(new BorderLayout(0, 0));
JSplitPane splitPane = new JSplitPane();
frame.getContentPane().add(splitPane, BorderLayout.CENTER);
JPanel canvasPanel = new JPanel();
canvasPanel.setLayout(new BorderLayout(0, 0));
splitPane.setRightComponent(canvasPanel);
canvas = new Canvas() {
private static final long serialVersionUID = -1069002023468669595L;
public void removeNotify() {
stopOpenGL();
}
};
canvas.addComponentListener(new ComponentListener() {
public void componentShown(ComponentEvent e) {
setNeedValidation();
}
public void componentResized(ComponentEvent e) {
setNeedValidation();
}
public void componentMoved(ComponentEvent e) {
setNeedValidation();
}
public void componentHidden(ComponentEvent e) {
setNeedValidation();
}
});
canvas.setIgnoreRepaint(true);
canvas.setPreferredSize(new Dimension(800, 600));
canvas.setMinimumSize(new Dimension(320, 240));
canvas.setVisible(true);
canvasPanel.add(canvas, BorderLayout.CENTER);
JScrollPane scrollPane_1 = new JScrollPane();
scrollPane_1.setMinimumSize(new Dimension(160, 160));
splitPane.setLeftComponent(scrollPane_1);
JTree tree = new JTree();
scrollPane_1.setViewportView(tree);
startOpenGL();
}
private void setNeedValidation() {
needValidation = true;
needUpdateViewport = true;
}
private void startOpenGL() {
System.out.println("StartOpenGL");
gameThread = new Thread() {
public void run() {
try {
Display.create();
Display.setParent(canvas);
Rectangle rect = canvas.getBounds();
int w = (int) rect.getWidth();
int h = (int) rect.getHeight();
System.out.println("Display Rectangle: "+rect);
GL11.glClearColor(0.1f, 0.1f, 0.3f, 1);
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, w, h, 0, -1, 1);
GL11.glViewport(0, 0, w, h);
setupVertices();
running = true;
} catch (LWJGLException e) {
e.printStackTrace();
}
while (running) {
updateGL();
}
if (Display.isCreated()) {
Display.destroy();
}
}
};
gameThread.start();
}
private void setupVertices() {
vertices = new float[4 * 2];
vertices[0] = 0.1f;
vertices[1] = 0.3f;
vertices[2] = 0.2f;
vertices[3] = 0.8f;
vertices[4] = 0.9f;
vertices[5] = 0.6f;
vertices[6] = 0.7f;
vertices[7] = 0.05f;
}
private void updateGL() {
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
render();
Display.update();
Display.sync(60);
if (needUpdateViewport) {
needUpdateViewport = false;
Rectangle rect = canvas.getBounds();
int w = (int) rect.getWidth();
int h = (int) rect.getHeight();
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, w, h, 0, -1, 1);
GL11.glViewport(0, 0, w, h);
}
int error = GL11.glGetError();
if (error != GL11.GL_NO_ERROR) {
String msg = "Unknown Error";
switch (error) {
case GL11.GL_INVALID_OPERATION:
msg = "Invalid Operation"; break;
case GL11.GL_INVALID_VALUE:
msg = "Invalid Value"; break;
case GL11.GL_INVALID_ENUM:
msg = "Invalid Enum"; break;
case GL11.GL_STACK_OVERFLOW:
msg = "Stack Overflow"; break;
case GL11.GL_STACK_UNDERFLOW:
msg = "Stack Underflow"; break;
case GL11.GL_OUT_OF_MEMORY:
msg = "Out of memory"; break;
}
throw new RuntimeException(msg);
}
}
private void render() {
float scale = 100;
GL11.glBegin(GL11.GL_QUADS);
GL11.glColor4f(1, 0, 0, 1);
GL11.glVertex3f(vertices[0] * scale, vertices[1] * scale, 0);
GL11.glColor4f(1, 0, 0, 1);
GL11.glVertex3f(vertices[2] * scale, vertices[3] * scale, 0);
GL11.glColor4f(1, 0, 0, 1);
GL11.glVertex3f(vertices[4] * scale, vertices[5] * scale, 0);
GL11.glColor4f(1, 0, 0, 1);
GL11.glVertex3f(vertices[6] * scale, vertices[7] * scale, 0);
GL11.glEnd();
}
private void stopOpenGL() {
System.out.println("StopOpenGL");
running = false;
try {
gameThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void terminate() {
frame.dispose();
System.exit(0);
}
}
Thank you all in advance!
Hi Cornix, I have just tested your application on my 64-bit Windows 8 machine and it appears to work fine.
(http://bmatthew1.wikispaces.com/file/view/swing.lwjgl.example.png/490776388/swing.lwjgl.example.png)
Thank you for testing, I really appreciate.
Although it would be nice if some others could test this as well.
Quick test compiled in eclipse with 1.8:
(http://i.imgur.com/7oAv4KX.png)
At first the menu did show up in front of the canvas but when I when to do some stuff in the JTree it started showing behind. Will have a look later on
Hello,
Not sure if you're still interested in these tests, but anyway here you go:
Ubuntu 14.04 Trusty Tahr 64 bit
It seems to work fine sometimes, but other times the menu showed up behind the lwjgl canvas.
I am also interested in putting the lwjgl context in a swing app, so please tell me you've managed to make it work somehow :D
Not quite. Sometimes it works, sometimes it doesnt. So the "solution" I originally had is not really a solution but more of a sideeffect which sometimes takes place.
I have not yet found a way to really make it work without ever having any problems. But I am still looking of course.