user3226170
user3226170

Reputation: 175

Trouble with borders

I'm trying to get clarification as to how borders work, specifically the insets, and in searching through the Java docs and numerous websites I can't seem to find a clear explanation. Looking at this code:

import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;

public class ShadowWindow {

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

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

            JFrame frame = new JFrame("Testing");
            frame.setUndecorated(true);
            frame.setBackground(new Color(0, 0, 0, 0));
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setContentPane(new ShadowPane());

            JPanel panel = new JPanel(new GridBagLayout());
            panel.add(new JLabel("Look ma, no hands"));

            frame.add(panel);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    });
}

public class ShadowPane extends JPanel {

    public ShadowPane() {
        setLayout(new BorderLayout());
        setOpaque(false);
        setBackground(Color.BLACK);
        setBorder(new EmptyBorder(0, 0, 10, 10));
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(200, 200);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
        g2d.fillRect(10, 10, getWidth(), getHeight());
        g2d.dispose();
    }
}
}

The way I interpret it this is what is happening:

  1. JFrame frame (200 x 200) is created
  2. JPanel shadowPane is created (also 200 x 200) with an empty border of 10 pixels created on the inside bottom and inside right of the JPanel
  3. A second JPanel is created (200 x 200) and added on top of shadowPane
  4. A rectangle is drawn (200 x 200) starting at x = 10 and y = 10

So my question in how is the shadowPane going past the range of the JFrame? Does the border go 10 pixels outside the JFrame or does it exist inside the JFrame. From everything I've found it should be inside, but that doesn't make sense based on how this code generates a shadow behind the frame. can anybody walk me through this? Thanks.

Upvotes: 3

Views: 83

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347194

So my question in how is the shadowPane going past the range of the JFrame?

It's not. pack determines the preferred layout size of the content and makes the window big enough to accommodate it, because the frame is undercoated AND it's background is transparent, it "appears" as if the shadow hangs past the frame, it's an illusion.

The empty border is making sure that content added to the ShadowPane is "forced" into a small space.

Lets change the code slightly...

JFrame frame = new JFrame("Testing");
JPanel content = new JPanel(new BorderLayout());
content.setBackground(Color.RED);
//frame.setUndecorated(true);
//frame.setBackground(new Color(0, 0, 0, 0));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(content);
//frame.setContentPane(new ShadowPane());

JPanel panel = new JPanel(new GridBagLayout());
panel.add(new JLabel("Look ma, no hands"));

ShadowPane shadowPane = new ShadowPane();
shadowPane.add(panel);

frame.add(shadowPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);

What this does, is creates a new background panel, filled with the color red. It also adds the window decoration back in.

Shadow

As you can see, the shadow pane and the label are are all rendered within the confines of the window.

If we once again remove the window decoration...

Shadow

You can see that it's still the same...

So what's going on?

getPreferredSize is providing the core information about how the component would like to be size (in this case 200x200)

The EmptyBorder is defining a usable space within the ShadowPane which defines an area within which content can be displayed, it's leaving 10 pixels to the right and bottom of the component, in which components can't be displayed. This is take care of automatically by the layout manager. This means that the ShadowPane can actually paint here itself, but components added to it will never be displayed here, hence the shadow board.

Basically, it's smoke and mirrors and used to generate the illusion of a drop shadow behind the content added to the frame (or the ShadowPane in this case)

Upvotes: 5

Related Questions