LogicalKip
LogicalKip

Reputation: 522

Weird/different behaviour when updating JDialog

On a project, I have a JFrame A starting a JDialog B, which itself starts a JDialog C (all using buttons). However, when following one of these procedures :

what is displayed in B is not the same (the second procedure gives a weird ugly thing).

I don't get why that could be, since in both ways my updateAll method is called.

I tried to recreate this with a little program so that it's easier to see what's going on. Solving this may or may not solve it on my actual project, but it will surely help.

Since I don't know where it could come from, here's the full code of my (test) program. Brace yourselves.

The 'A' Frame

public class MyFrame extends JFrame {
    private static final long serialVersionUID = 7073064926636937881L;
    public MyFrame() {
        this.setSize(200, 300);
        JButton button = new JButton("Click me");
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                new MyDialog1().setVisible(true);
            }
        });
        this.getContentPane().add(button);
    }

    public static void main(String args[]) {
        new MyFrame().setVisible(true);
    }
}

The 'B' Dialog

 public class MyDialog1 extends JDialog {
        private static final long serialVersionUID = 9181006217120036637L;
        private JScrollPane scrollPane;
        public String text = "aaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaa";

    public MyDialog1() {
        this.setVisible(false);
        this.setSize(800, 600);

        this.initComponent();
        this.updateAll();
    }

    private void initComponent() {
        this.getContentPane().setLayout(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();

        this.scrollPane = new JScrollPane();

        c.gridx = 0;
        c.gridy = 0;
        this.getContentPane().add(this.scrollPane, c);

        c.gridx = 0;
        c.gridy = 1;
        JButton b = new JButton("Supposedly edit stuff");
        final MyDialog1 caller = this;
        b.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                new MyDialog2(caller).setVisible(true);
            }
        });
        this.getContentPane().add(b, c);

        c.gridx = 1;
        c.gridy = 1;
        b = new JButton("Leave");
        b.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                setVisible(false);
            }
        });
        this.getContentPane().add(b, c);
    }

    public void updateAll() {
        JPanel mainPanel = new JPanel();
        for (int i = 0 ; i < 5 ; i++) {
            JPanel subPanel = new JPanel();
            JTextArea t = new JTextArea(this.text);
            t.setSize(60, 30);
            t.setVisible(true);// Useful ? What about setSize ?
            subPanel.add(t);
            mainPanel.add(subPanel);
        }
        this.scrollPane.setSize(150, 150); // FIXME When in initComponent, doesn't do anything, and when in updateAll, behavior is inconsistent 
        this.scrollPane.setViewportView(mainPanel); // Replacing previous JPanel

    }
}

The 'C' Dialog

public class MyDialog2 extends JDialog {
    private static final long serialVersionUID = 5676648412234106581L;
    private MyDialog1 caller;

    public MyDialog2(MyDialog1 c) {
        this.setSize(100, 150);
        this.caller = c;
        JButton cancelButton = new JButton("Cancel");
        cancelButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                setVisible(false);
                caller.text += "\nbbbbbbbbbbbbb\nbbbbbbbbbbbbbbbbbb\nbbbbbbbbbbb\nbbbbbbbbbbbbbb\ncccccccccccccccccccccccccccccccccccccccccccc\ncccccccccc";
                caller.updateAll();
            }
        });
        this.getContentPane().add(cancelButton);
    }
}

Thanks for your help.

Upvotes: 0

Views: 56

Answers (2)

VGR
VGR

Reputation: 44414

Easy answer which ignores good practice:

Replace this:

this.scrollPane.setSize(150, 150);

with this:

this.scrollPane.setMinimumSize(new Dimension(150, 150));

Calling setSize on a component which is in a Container with a layout usually does nothing; at best, it will set the size until the next time the container is validated, since the size will be overwritten by the layout manager. However, setting a minimumSize (or preferredSize or maximumSize) sets a persistent property that is respected by (most) layout managers.

Why does setMinimumSize make a difference? Because you have not set any weightx or weighty properties on any of your GridBagConstraints, so there is not enough room for the GridBagLayout to display your JScrollPane at its preferred size. When GridBagLayout determines there isn't enough room to display everything at the size it requires, the layout "punts" and forces everything to revert to its minimum size.

The first time you display the B dialog, you are seeing the JScrollPane at its minimum size (that is, just large enough to display the scrollbars and viewport border). After canceling the C dialog, the setSize(150, 150) takes effect, but it's likely that any subsequent changes to any descendant of the GridBagLayout would cause that (150, 150) to be overwritten with the JScrollPane's minimum size again.

More complex answer which is good practice:

Remove all calls to setSize in every class. Usually you should not set explicit sizes at all, but if you must do so, use setPreferredSize.

Components which are made to grow and shrink, like JScrollPanes, should be assigned positive weightx and weighty values in their corresponding GridBagConstraints, along with fill being set to GridBagConstraints.BOTH as pcej suggested.

Upvotes: 0

pcejrowski
pcejrowski

Reputation: 622

I suggest to use

c.gridx = 0;
c.gridy = 0;
c.fill = GridBagConstraints.BOTH; // make the component fill its display area entirely
c.ipady = 150; //height
c.anchor = GridBagConstraints.FIRST_LINE_START; // component start from the left top corner
this.getContentPane().add(this.scrollPane, c);

for definition of JScrollPane constraints.

In additition, add validate() and repaint() after modifying elements

this.scrollPane.setViewportView(mainPanel); // Replacing previous JPanel
this.validate();
this.repaint();

Upvotes: 1

Related Questions