3

I have a JPanel called pnlMain with a layout set to BorderLayout. Inside the panel are three JPanel added to PAGE_START, CENTER, and PAGE_END. Now, my requirement is if the Settings button is clicked, it will display a transparent JPanel above pnlMain. This transparent panel will then contain an opaque, smaller, centered panel, that will contain the settings stuff.

I know I can do this using JLayeredPane, but looking at the tutorial it says that you can only put components of different depth using absolute positioning which I'm aware is highly discouraged.

Is there some other way to do this without using absolute positioning?

Bnrdo
  • 5,325
  • 3
  • 35
  • 63
  • FYI- You can use a layout manager with `JLayeredPane`, but you'd need a layout manager that will allow you to place components ontop of each other, like `GridBagLayout`, but there are probably easier solutions – MadProgrammer Jun 26 '13 at 03:07

1 Answers1

7

You can use the glass pane of the parent frame, which will allow you to add components to it that will appear to overlaid over the main content.

Basically, I would, create a JPanel and set it to be transparent (setOpaque(false)). I would set it's layout manager to something like GridBagLayout (as it will use the preferred size of the child component and center it automatically within it's parent container).

Onto this panel I would then add the Settings panel.

Finally, I would set the parent frame's glass pane to the first (backing) pane and make it visible.

frame.getRootPane().setGlassPane(backingPane); // Or similar

Take a look at How to use Root Panes

Updated

If you can't use the glass pane of the top level frame yourself, then you need to fake it.

This example basically uses a JLayeredPane backed by a GridBagLayout

enter image description here

If you add a MouseListener and KeyListener to the background pane you can consume events going to the child components.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;

public class FloatingPane {

    public static void main(String[] args) {
        new FloatingPane();
    }

    public FloatingPane() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                final WorkPane workPane = new WorkPane();
                JButton settings = new JButton("Settings");
                settings.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        workPane.toggleSettings();
                    }
                });

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(workPane);
                frame.add(settings, BorderLayout.SOUTH);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class WorkPane extends JLayeredPane {
        private final BackingPane backingPane;

        public WorkPane() {

            setLayout(new GridBagLayout());

            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 1;
            gbc.gridy = 0;
            gbc.weightx = 1;
            gbc.weighty = 1;
            gbc.fill = GridBagConstraints.BOTH;
            add(createLabel("Center", Color.BLUE), gbc);

            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.weightx = 0;
            gbc.weighty = 0;
            gbc.fill = GridBagConstraints.VERTICAL;
            add(createLabel("Left", Color.RED), gbc);
            gbc.gridx = 2;
            add(createLabel("Right", Color.GREEN), gbc);

            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.weightx = 1;
            gbc.weighty = 1;
            gbc.gridheight = GridBagConstraints.REMAINDER;
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            gbc.fill = GridBagConstraints.BOTH;

            backingPane = new BackingPane();
            backingPane.add(new SettingsPane());
            backingPane.setVisible(false);
            add(backingPane, gbc);

            setLayer(backingPane, DEFAULT_LAYER + 1);

        }

        public void toggleSettings() {

            backingPane.setVisible(!backingPane.isVisible());

        }

        protected JLabel createLabel(String text, Color bg) {

            JLabel label = new JLabel(text);
            label.setHorizontalAlignment(JLabel.CENTER);
            label.setOpaque(true);
            label.setBackground(bg);

            return label;

        }
    }

    public class BackingPane extends JPanel {

        public BackingPane() {
            setLayout(new GridBagLayout());
            setOpaque(false);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(new Color(128, 128, 128, 192));
            g.fillRect(0, 0, getWidth(), getHeight());
        }

    }

    public class SettingsPane extends JPanel {

        public SettingsPane() {

            setBorder(new EmptyBorder(10, 10, 10, 10));
            add(new JLabel("Settings"));

        }
    }
}

Another solution might be to fake the entire glass pane by taking a snap shot of the current panel and using a CardLayout, flip to the settings pane, using the snap shot as the backgound image for the settings pane (which can could then apply effects to like gray scaling and bluring)

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • 1
    :D At least I helped some one today – MadProgrammer Jun 26 '13 at 03:35
  • Mr. @MadProgrammer is there a way to do this in `JPanel` layer because my `JPanel` might be embedded in a bigger frame, that means if I call on the `getRootPane()` it will return the bigger frame then the glasspane will be applied to the bigger frame. Is it not? – Bnrdo Jun 26 '13 at 03:42
  • @onepotato [agree](http://stackoverflow.com/a/9734016/714968), but for Java7 to use [JLayer](http://docs.oracle.com/javase/tutorial/uiswing/misc/jlayer.html) – mKorbel Jun 26 '13 at 05:06
  • @mKorbel As I understand it, `JLayer` is a decorator, which doesn't allow for real time interaction with components (stuff is just painted) - could be wrong as I've only used `JXLayer` – MadProgrammer Jun 26 '13 at 05:11
  • @MadProgrammer 1. agree, yes from this point of view, this is true, is about illusion done by paint/paintComponent/paintIcon/paintChildren, there aren't accesible Key/MouseEvents directly in compare with GLassPane, 2. back to the end still no idea where going OPS sentence == `that will the settings stuff.`, then reason for my comment here – mKorbel Jun 26 '13 at 05:24
  • @mKorbel Cool, just want to make sure :P – MadProgrammer Jun 26 '13 at 05:34
  • @onepotato I've added an additional example based on my comment to the main question – MadProgrammer Jun 26 '13 at 05:34
  • more than basic stuff about JLayer is [l(as always) in post by @aterai](http://stackoverflow.com/search?q=user%3A177145+jlayer) – mKorbel Jun 26 '13 at 05:40
  • @MadProgrammer Thank you for the additional example. I will use that instead. – Bnrdo Jun 26 '13 at 06:07
  • @MadProgrammer +1 thanks, I found it interesting and trying to learn from it. Could you explain the benefit of overriding paintComponent(Graphics g) in BackingPane, over simply setting setBackground(new Color(128, 128, 128, 192)); ? – c0der Apr 04 '16 at 10:06
  • @OferYuval Swing can't handle alpha based colors when applied to the background color of a component, Swing components are either opaque or they're not. This is track, making the component transparent, we then use `paintComponent` to paint the translucent color ourselves, thus preserving the way painting works and getting the desired effect – MadProgrammer Apr 04 '16 at 10:14
  • @MadProgrammer thanks for the quick response. When I omit setOpaque(false); and add setBackground(new Color(128, 128, 128, 192)); in the constructor, and also omit paintComponent(Graphics g) , I get the same visual result. What do I miss ? – c0der Apr 04 '16 at 10:28
  • 2
    @OferYuval Try adding other components to the UI and trigger some repaints, especially those which are supposed to occur underneath the component. You may not always get a glitch, but when you do, it will be tremendously awesome and the which is source of more than a few questions on SO – MadProgrammer Apr 04 '16 at 10:32