The below code creates a frame with a JTabbedPane in the frame's contentPane and a "Help" button added directly to the frame's layered pane - at a higher index than the default, so it should always be painted in front of the tabbed pane. But as you can see if you run it, as soon as you click on one of the tabs (or, on Mac, as soon as you hover over a tab), the "Help" button gets painted over - i.e. disappears. You have to resize the frame to make the "Help" button re-appear.
Is this a Java bug or am I doing something wrong? If the latter, what needs to be done to fix the problem? I've consulted both https://docs.oracle.com/javase/tutorial/uiswing/components/rootpane.html and https://docs.oracle.com/javase/tutorial/uiswing/components/toplevel.html .
Please pardon the poor positioning of the "Help" button. I was just trying to write a quick test to include here. The idea is the to overlay a "Help" icon button in the unused space of the JTabbedPane.
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
public class TabTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
JTabbedPane tp = new JTabbedPane();
tp.addTab("hello", new JPanel());
tp.addTab("there", new JPanel());
frame.getContentPane().add(tp, BorderLayout.CENTER);
JLayeredPane layeredPane = frame.getRootPane().getLayeredPane();
JButton helpButton = new JButton("Help");
helpButton.setBounds(800, 5, 50, 20);
layeredPane.add(helpButton, 400);
frame.setSize(new Dimension(900, 800));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
This is a common misunderstanding with JLayeredPane
.
If you have a look at the JavaDocs for Container
, you will note that there are (at least) two add
methods
The question you need to answer is, which one are you actually calling?
Both the JavaDocs for JLayeredPane
and How to use JLayeredPane demonstrate that you should be calling the second one.
While the first can affect the z-ordering of the components, it's no guarantee that the component positions won't be changed.
Instead of:
layeredPane.add(helpButton, 400);
you should be using:
layeredPane.add(helpButton, new Integer(400));
which will pass the value as a constraint to the container, instead of the desired position within the container hierarchy - yeah, suitable I know
An alternative solution might be to use the glassPane
instead, for example...
JFrame frame = new JFrame();
JTabbedPane tp = new JTabbedPane();
tp.addTab("hello", new JPanel());
tp.addTab("there", new JPanel());
frame.getContentPane().add(tp, BorderLayout.CENTER);
// Null layout used here for demonstration purposes only
JPanel glassPane = new JPanel(null);
glassPane.setOpaque(false);
frame.getRootPane().setGlassPane(glassPane);
// This is important, as setGlassPane makes it invisible
glassPane.setVisible(true);
JButton helpButton = new JButton("Help");
helpButton.setBounds(800, 5, 50, 20);
glassPane.add(helpButton);
frame.setSize(new Dimension(900, 800));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);