1

So I've made a class WindowDisp which extends JFrame:

public class WindowDisp extends JFrame{

    private static final long serialVersionUID = 3245091489595286109L;

    private int height, width;
    private JPanel panel;
    private JLabel mainPane;

    public WindowDisp(int a, int b, int pw, int ph){
        height = a;
        width = b;

        Container c = getContentPane();
        c.setPreferredSize(new Dimension(width, height));
        pack();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setResizable(false);
        setLocationRelativeTo(null);

        mainPane = new JLabel();
        mainPane.setPreferredSize(new Dimension(pw, ph));
        mainPane.setFont(new Font("Monospaced", Font.PLAIN, 10));

        panel = new JPanel();
        panel.setPreferredSize(getSize());
        panel.add(mainPane);
        add(panel);

        setVisible(true);
        pack();
    }

    public static void main(String[] args){
        WindowDisp win = new WindowDisp(400, 400, 400, 400);
    }
}

From my Main class, I declare a WindowDisp where its height and width are equal to its ph and pw. The problem, however, is that, upon running my program, white 'bars' appear around the default background colored JPanel in the frame. They appear to be padding the panel from the right and the bottom, as though there is space in the frame that the panel is not occupying, although, if my coding is correct, the panel should be the same size as the frame's ContentPane, should it not?

I've found that removing either of the two pack(); commands does not remove these bars, although removing the first one changes them to black, and removing the second widens the one on the right. Of course, removing both of them causes the frame not to be the same size as its ContentPane. Furthermore, removing the add(panel); altogether has no effect.

I can't figure out what in this code is causing that seemingly empty space to appear in my frame, and, again, in my program, all four values being passed to the Window constructor are equal. What seems really strange is that, even if I just remove the add(panel);, nothing at all changes visa vi the white padding. In fact, I can comment out everything from mainPane =... to add(panel); and that doesn't affect it at all.

user2649681
  • 750
  • 1
  • 6
  • 23
  • @AndrewThompson Would what I've added suffice as an SSCCE? – user2649681 Feb 18 '15 at 01:05
  • Seems to work just fine for me. What OS? What Java Version – MadProgrammer Feb 18 '15 at 01:06
  • Windows 8.1 x64, Java 7. The program runs and does almost exactly what I want it to, save for the white what-appears-to-be-padding around the bg-colored portion of the `JFrame` – user2649681 Feb 18 '15 at 01:07
  • Sounds like an issue with `setResizable`, but I can't replicate it – MadProgrammer Feb 18 '15 at 01:08
  • This `panel.setPreferredSize(getSize());` is really dangerous, as the size of the window is different from that of the viewable space. – MadProgrammer Feb 18 '15 at 01:09
  • @MadProgrammer Even if I try changing it to `panel.setPreferredSize(getContentPane().getPreferredSize());` nothing changes, the white 'padding' is still there. – user2649681 Feb 18 '15 at 01:10
  • *"Would what I've added suffice as an SSCCE?.."* Not if there are non J2SE classes (revealed most obviously in the imports) or if there is a stack trace to match up to the code lines (where the number of lines of imports becomes relevant). But in this case? Yes I reckon that should do.. – Andrew Thompson Feb 18 '15 at 01:11
  • 2
    Let's start with [Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?](http://stackoverflow.com/questions/7229226/should-i-avoid-the-use-of-setpreferredmaximumminimumsize-methods-in-java-swi). You shouldn't be screwing with these values, second, there is a difference between the window size and the viewable content size, as the viewable content needs to take into account the frames decorations – MadProgrammer Feb 18 '15 at 01:12
  • 1) `new Font("Monospaced", Font.PLAIN, 10)` should be `new Font(Font.MONOSPACED, Font.PLAIN, 10)` for compile time checking. 2) See [Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?](http://stackoverflow.com/q/7229226/418556) (Yes.) – Andrew Thompson Feb 18 '15 at 01:12
  • @AndrewThompson Alright but let's say I completely avoid these methods, how do I ensure that everything in the frame, including the frame itself, is the size I want it to be? Even if I use a `LayoutManager`, won't that only affect what's within the frame, and not the size of the frame itself? – user2649681 Feb 18 '15 at 01:14
  • Don't worry about the frame, worry about how much space you want/need for it's content. Each OS has different decoration requirements, so the amount of space that a window physically needs is different for each OS – MadProgrammer Feb 18 '15 at 01:16
  • Use layout padding and borders for [white space](http://stackoverflow.com/a/17874718/418556). Finally, call `pack()` to ensure the frame is exactly as large as (the smallest size) it needs to be in order to display the components and white space. – Andrew Thompson Feb 18 '15 at 01:17
  • Can you provide a code example which has the panel sized to 400 x 400 pixels and the frame's content(size w/o decorations) to the same dimension? – user2649681 Feb 18 '15 at 01:18
  • @user2649681 The only way a window could ever match the content size is if the window was undecorated – MadProgrammer Feb 18 '15 at 01:33

4 Answers4

3

I can't seem to replicate the issue exactly, but I think I can replicate the desired results...

enter image description here

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
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;
import javax.swing.border.LineBorder;

public class WindowDisp extends JFrame {

    private static final long serialVersionUID = 3245091489595286109L;

    private JPanel panel;
    private JLabel mainPane;

    public WindowDisp(int width, int height) {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setResizable(false);

        mainPane = new JLabel();
        mainPane.setBorder(new LineBorder(Color.BLUE));
        mainPane.setFont(new Font("Monospaced", Font.PLAIN, 10));

        panel = new JPanel() {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(width, height);
            }
        };
        panel.setLayout(new BorderLayout());
        panel.setBorder(new EmptyBorder(4, 4, 4, 4));
        panel.add(mainPane);
        add(panel);

        pack();
        setLocationRelativeTo(null);
        setVisible(true);

        System.out.println(getSize());
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                WindowDisp win = new WindowDisp(400, 400);
            }
        });
    }
}

Things that jump out at me (as been of issue)...

  • panel.setPreferredSize(getSize()); - The size of the window is generally larger than the window size, this is because the window has decorations which are painted WITHIN the frame boundaries. pack uses the layout information (and preferredSize of the components indirectly) to ensure that the content has the amount of space that it asks for, it then sizes the window around this to accommodate the frame decorations. By calling getContentPane().setPreferredSize you are superseding any information that the layout manager might provide and ignoring the requirements of the other components. This is one of the (many) reasons why we recommend that you NEVER call setPreferredSize, ever...

To reiterate...

Container c = getContentPane();
c.setPreferredSize(new Dimension(width, height));

Forces the viewable space of the window to be set to the width/height values (400x400). This container will no longer be able to react to changes to it's content and will ALWAYS prefer to be 400x400

mainPane.setPreferredSize(new Dimension(pw, ph));

Sets the preferred size of the mainPane to 400x400 (based on your example). Again, it will ALWAYS prefer to be 400x400, but the simple fact of setting the content pane, means that this value is actually ignored...

panel.setPreferredSize(getSize());

Now, the nail in the coffin. This sets the panel to be the same size of the frame, BUT, the frame is larger than the contentPane (400x400 + frame decorations), it is also offset within frame (it won't be positioned at 0x0, but will be offset so that it appears below the frame's title and right border), but could expand beyond the frames boundaries

This combination of issues are all working against you. Instead of worrying about the frame size, worry about the size needs/requirements of the what the frame displays.

Each OS uses different frame decorations, so the actual, physical, frame size will be different on different OSs, but if you focus on the requirements of the content, you won't care

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Nope. While the blue border around the label leaves no empty space horizontally, there's still a big block of empty white space at the bottom of the window(with no blue border line). – user2649681 Feb 18 '15 at 01:27
  • Then, you are doing something else which you're not showing, cause it works just fine for me. Windows 7, Java 8 – MadProgrammer Feb 18 '15 at 01:31
  • @user2649681 The other problem is you've not been able to provide us (or at least me) an example which demonstates your problem :P – MadProgrammer Feb 18 '15 at 01:46
  • The example I provided, at least when I ran it, demonstrated the problem. There was no other code I left out, so perhaps my operating system is messing something up. Also, I'm running Java 7. – user2649681 Feb 18 '15 at 02:07
  • Have you tried removing `setResizable(false)`? I've tested both your code and my code on Java 7 and Java 6 without getting the white border – MadProgrammer Feb 18 '15 at 02:25
  • Yeah I've tried it, didn't make a difference. By this point I've just switched to ensuring the contents of the label are the proper dimensions and removing everything else that sets size. – user2649681 Feb 18 '15 at 02:31
2

See Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing? (Yes.)

Instead use layout padding and borders for white space. Finally, call pack() to ensure the frame is exactly as large as (the smallest size) it needs to be in order to display the components and white space.

WindowDisp

import java.awt.Font;
import javax.swing.*;
import javax.swing.border.EmptyBorder;

public class WindowDisp extends JFrame {

    private JPanel panel;
    private JLabel mainLabel;

    public WindowDisp(int t, int l, int b, int r, String title) {
        super(title);
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        setResizable(false);
        setLocationByPlatform(true);

        mainLabel = new JLabel("Hello Padding!");
        mainLabel.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 10));
        mainLabel.setBorder(new EmptyBorder(t, l, b, r));

        panel = new JPanel();
        panel.add(mainLabel);
        add(panel);

        pack();
        setVisible(true);
    }

    public static void main(String[] args) {
        Runnable r = new Runnable() {

            @Override
            public void run() {
                new WindowDisp(50, 150, 50, 150, "Window 1");
                new WindowDisp(50, 100, 50, 100, "Window 2");
            }
        };
        SwingUtilities.invokeLater(r);
    }
}
Community
  • 1
  • 1
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
1

So far the best patch for this annoying issue is the following. Doesn't matter where you call the setResizable(false) method. Just add this piece of code after you setVisible(true).

private void sizeBugPatch() {
    while (frame.getWidth() > yourWidth) {
        frame.pack();
    }
}

Where yourWidth is the width you've set in any of the possible ways, either manually or by overriding setPreferredSize methods. The explanation is quite easy, frame.pack() seems to reset frame.setResizable(boolean b) somehow. You could use an if instead of the while loop but I prefer while to exclude the case the window would still be extra-sized even after a second pack().

Andrei Mesh
  • 256
  • 2
  • 20
0

One thing I have learned from swing days is to never mix setPreferred... with pack(). You either use one or the other.

Try this code:

import javax.swing.*;
import java.awt.*;

public class WindowDisp extends JFrame {

    private int height, width;
    private JPanel panel;
    private JLabel mainPane;

    public WindowDisp(int a, int b, int pw, int ph){
        height = a;
        width = b;

        Container c = getContentPane();
//        c.setPreferredSize(new Dimension(width, height));

        mainPane = new JLabel("Hello from Pane");
        mainPane.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 10));

        panel = new JPanel();
//        panel.setPreferredSize(new Dimension(pw, ph));
        panel.add(mainPane);
        c.add(panel, BorderLayout.CENTER);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setResizable(false);
        setLocationRelativeTo(null);
        pack();
        setVisible(true);
    }

    public static void main(String[] args){
        WindowDisp win = new WindowDisp(400, 400, 400, 400);
    }
}

Output:

enter image description here

smac89
  • 39,374
  • 15
  • 132
  • 179
  • `pack` uses the preferred size of the content (as defined by the results of the layout manager) to make decisions about the size of the window... – MadProgrammer Feb 18 '15 at 01:20
  • Alright... so how do I ensure that the window is 400 x 400(or any constant size)? In this example, it's just as big as it needs to be to accommodate the JLabel. – user2649681 Feb 18 '15 at 01:22
  • @user2649681 Uncomment the second thing I commented out in the code – smac89 Feb 18 '15 at 01:22
  • @MadProgrammer you're right. I guess I was thinking of setMax|Min|Size, options – smac89 Feb 18 '15 at 01:24
  • @Smac89 I uncommented it, and the window is closer to the right size, but there's still an ugly white chunk of nothing at the bottom. – user2649681 Feb 18 '15 at 01:29
  • @user2649681 If you didn't add anything to the bottom, how can you expect anything to be there? Add an image or something in the panel which takes up the remaining space and see if you still get the same empty space. At the moment the panel only contains a label which is very tiny in comparison the rest of the panel – smac89 Feb 18 '15 at 01:31