0

I am trying to add an OpenGL context to a jFrame.

It works when I do it like this:

jframe.setVisible( true );
jframe.getContentPane().add( glcanvas, BorderLayout.CENTER );
jframe.revalidate();
jframe.repaint();

However if I try to create the context on a button click like this:

JButton startButton = new JButton("Start");
startButton.addActionListener(
    new ActionListener()
{
    @Override
    public void actionPerformed(ActionEvent e) 
    {
            jframe.getContentPane().add( glcanvas, BorderLayout.CENTER );
        jframe.revalidate();
        jframe.repaint();
        }
});
jframe.add(startButton);

then nothing happens. If I try and debug it the program runs through the revalidate() and repaint() commands but nothing changes.

Can someone please tell me why this is happening - why is it different when I try to call it on a button press? Also can you suggest a way to fix this? I need to be able to open the context from a menu system.

-- Edited: Here is the full code of my test program

/* -- OpenGL -- */
import javax.media.opengl.GL2ES2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLProfile;
import javax.media.opengl.awt.GLCanvas;
import javax.media.opengl.awt.GLJPanel;
import javax.swing.JComponent;

/* -- Swing -- */
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JLabel;

import javax.swing.JPanel;

/* -- jFrame Layouts -- */
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class Menu 
{
public static void main(String[] args) 
{

        GLProfile glprofile = GLProfile.getDefault();
        GLCapabilities glcapabilities = new GLCapabilities( glprofile );
        final GLCanvas glcanvas = new GLCanvas( glcapabilities );

        glcanvas.addGLEventListener( new GLEventListener() 
        {
            @Override
            public void init( GLAutoDrawable glautodrawable ) 
            {
                System.out.println("INIT");
            }

            @Override
            public void reshape( GLAutoDrawable glautodrawable, int x, int y, int width, int height ) 
            {
                GL2ES2 gl = glautodrawable.getGL().getGL2ES2(); //Get OpenGL

                gl.glViewport( 0, 0, width, height );   //Set the viewport
            }

            @Override
            public void display( GLAutoDrawable glautodrawable ) 
            {
                GL2ES2 gl = glautodrawable.getGL().getGL2ES2();

                gl.glClearColor(+0.0f, +0.2f, +0.9f, +0.0f);  // Blue background

                //Clear the screen so that we can draw the new one
                gl.glClear(
                    GL2ES2.GL_STENCIL_BUFFER_BIT |
                    GL2ES2.GL_COLOR_BUFFER_BIT   |
                    GL2ES2.GL_DEPTH_BUFFER_BIT   
                );
            }

            @Override
            public void dispose( GLAutoDrawable glautodrawable ) 
            {

            }

        }); /* Add GLEventListner to glCanvas */

        final JFrame jframe = new JFrame( "One Triangle Swing GLCanvas" ); 
        jframe.addWindowListener( new WindowAdapter() 
        {
            public void windowClosing( WindowEvent windowevent ) 
            {
                jframe.dispose();
                System.exit( 0 );
            }
        });        

        jframe.setSize( 800, 600 );
        jframe.setVisible( true );

        /* --- IF I ADD THE ContentPane HERE AND REVALIDATE/REPAINT IT WORKS --- */
        //jframe.getContentPane().add( glcanvas, BorderLayout.CENTER );
        //jframe.revalidate();
        //jframe.repaint();

        jframe.setLayout(new FlowLayout());

        JButton startButton = new JButton("Start");
        startButton.addActionListener(
            new ActionListener()
            {
                @Override
                public void actionPerformed(ActionEvent e) 
                {     
                    /* --- I NEED TO ADD THE ContentPane HERE, BUT IF I DO THIS THE REPAINT DOESN'T WORK --- */                          
                    jframe.getContentPane().add( glcanvas, BorderLayout.CENTER );
                    jframe.revalidate();
                    jframe.repaint();

                        /* -- I added this because I was told elsewhere that repainting 
                         *    in a different thread would make it work, however I haven't
                         *    been successful in getting this to work. Included for information
                         */
                    //repaintThread(jframe);

            }
            });
        jframe.add(startButton);      

}

private static void repaintThread(final JFrame jframe) 
    {  
    Thread thread = new Thread(new Runnable() 
    {  
        public void run() 
        {  
            for( int i = 0; i < 200; i++)
            {
                jframe.revalidate();
                jframe.repaint();


                    try
                    {  
                        Thread.currentThread().sleep( 50 );  
                    } 
                    catch( Exception ex )
                    {  
                        break;  
                    }
            }
        }  
    });

    thread.setPriority(Thread.NORM_PRIORITY);  
    thread.start();  
}  

}
Francis
  • 1,151
  • 1
  • 13
  • 29
  • `setVisible(true);` should be last. – BitNinja May 09 '14 at 00:14
  • Yes, I know that, but for testing I wanted to make the frame visible first and then add more content to it. In my program I have to make the frame visible first in order to display the buttons that are needed to add the OpenGL context. – Francis May 09 '14 at 00:21
  • 2
    1) For better help sooner, post an [MCVE](http://stackoverflow.com/help/mcve) (Minimal Complete and Verifiable Example). 2) For many components in one space, use a [`CardLayout`](http://docs.oracle.com/javase/7/docs/api/java/awt/CardLayout.html) as seen in this [short example](http://stackoverflow.com/a/5786005/418556). – Andrew Thompson May 09 '14 at 01:15
  • You don't add an OpenGL context, you add a JOGL AWT GLCanvas which isn't the same thing. Please use an animator instead of creating a thread, look at my example: http://en.wikipedia.org/wiki/Java_OpenGL#Code_example – gouessej May 09 '14 at 09:50

1 Answers1

1

This is the line that messes everything up:

jframe.setLayout(new FlowLayout());

You're changing the layout to FlowLayout and then adding glcanvas next to startButton. The preferred size of glcanvas is 0 so it is not visible, although you can see a slight movement of startButton when the button is clicked.

I assume that setting FlowLayout was not intentional because you are using BorderLayout.CENTER constraint to add glcanvas. So here is a simple fix:

Comment out:

//jframe.setLayout(new FlowLayout());

Add startButton to the NORTH of the panel instead of a CENTER since glcanvas will go to the CENTER. Change:

jframe.add(startButton);

to:

jframe.add(startButton, BorderLayout.NORTH); 

That fixed the revalidation issue, at least on Windows 7 Java 7.

Also, see A Visual Guide to Layout Managers for more details and examples about layouts.

tenorsax
  • 21,123
  • 9
  • 60
  • 107