Bug 1307

Summary: GLCanvas is always drawn on top when using a JTabbedPane
Product: [JogAmp] Jogl Reporter: Robin Stevens <robin.stevens>
Component: awtAssignee: Sven Gothel <sgothel>
Status: RESOLVED FIXED    
Severity: critical CC: gouessej
Priority: P4    
Version: 2.4.0   
Hardware: All   
OS: macosx   
Type: DEFECT SCM Refs:
jogl d590c5df17650b3790bb434fb7529df874914a09
Workaround: ---
Bug Depends on: 1299    
Bug Blocks:    

Description Robin Stevens 2016-05-13 12:33:33 CEST
Java version:
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)

Operating system:
OS X El Capitan
10.11.4

When using a JTabbedPane with two tabs (one with regular JPanel, one with a GLCanvas), the GLCanvas is always painted on top. Even when selecting the tab with the regular JPanel, the GLCanvas is painted on the screen and you do not see the regular JPanel.

Code to reproduce this:

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.WindowConstants;

import com.jogamp.opengl.awt.GLCanvas;

public class JOGLTabbedPaneTest {
  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      @Override
      public void run() {
        showUI();
      }
    });
  }

  private static void showUI(){
    JFrame testFrame = new JFrame("TestFrame");

    JTabbedPane tabbedPane = new JTabbedPane();

    JPanel lightweight = new JPanel(){
      @Override
      protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.PINK);
        g.fillRect(0,0, getWidth(), getHeight());
      }

      @Override
      public Dimension getPreferredSize() {
        return new Dimension(200,200);
      }
    };
    tabbedPane.addTab("LW", lightweight);

    GLCanvas glCanvas = new GLCanvas();
    tabbedPane.addTab("HW", glCanvas);

    testFrame.getContentPane().add(tabbedPane);

    testFrame.pack();
    testFrame.setVisible(true);
    testFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
  }
}


This sounds very similar to bug 1277 (https://jogamp.org/bugzilla/show_bug.cgi?id=1277) as the problem disappears when you call the showUI method on the main thread instead of on the EDT.

The workaround posted in bug 1277 (removing and readding the GLCanvas from/to the Swing hierarchy) is not sufficient to fix this bug.

The only workaround I found for now is to remove the GLCanvas from the Swing hierarchy when its parent container is not showing, and re-add it when the parent is showing.
As you get no events when a container is showing/not showing, I use a timer to periodically poll the showing state of the parent container:

  private static void startRemoveHeavyWeightComponentFromContainerWhenContainerNotShowingTimer(final Container aContainer, final Component aComponent){
    Timer timer = new Timer(500, new ActionListener(){
      private final WeakReference<Container> fContainer = new WeakReference<Container>(aContainer);
      private final WeakReference<Component> fComponent = new WeakReference<Component>(aComponent);
      @Override
      public void actionPerformed(ActionEvent e) {
        Container container = fContainer.get();
        Component component = fComponent.get();
        if ( container == null || component == null ){
          ((Timer) e.getSource()).stop();
          return;
        }
        boolean containerContainsComponent = containerContains(container, component);
        if ( container.isShowing() && !containerContainsView ){
          container.add(component);
          container.revalidate();
          container.repaint();
        } else if ( !container.isShowing() && containerContainsView ){
          container.remove(component);
          container.revalidate();
          container.repaint();
        }
      }
    });

    timer.setRepeats(true);
    timer.start();
  }

  private static boolean containerContains( Container aContainer, Component aComponent ){
    Component[] components = aContainer.getComponents();
    for (Component component : components) {
      if (component == aComponent) {
        return true;
      }
    }
    return false;
  }
Comment 1 Robin Stevens 2016-05-17 14:51:33 CEST
I just created a new build locally from the master branch on Github with patch https://jogamp.org/bugzilla/attachment.cgi?id=786 applied.

After only replacing jogl-all-natives-macosx-universal.jar in my project with the build/jar/jogl-all-natives-macosx-universal.jar from the custom build, this issue goes away.
Comment 2 Sven Gothel 2019-03-30 04:04:49 CET
Fix for bug 1299 also fixes this issue. Great