3

For my application I have a JDialog which contains a JPanel with some basic JTextFields and JButtons. The idea is to have a button which expands the JDialog to reveal a second JPanel which contains some "advance" settings.

I have achieved this with calling setPreferredSize() and pack(), but this isn't very elegant. An "elegant" solution would be to set the second Panel to be somehow null and therefore to get ignored by pack() when the toggle state is in "retracted".

Sorry that I can't provide you with code (that thing is about 700 lies) but all it is like I said basically two JPanels in a JDialog.

Here some pics:

enter image description hereenter image description here

Question: is there a trick to get this expanding thing going without having to set fixed dimensions for the expanded/normal state.

Haeri
  • 621
  • 1
  • 12
  • 27
  • You could use a `CardLayout` (for the expanded state) and call `pack` or you could add/remove the settings panel and call `pack`... – MadProgrammer Nov 12 '14 at 00:00
  • Hy MadProgrammer! Nice to see you again xD Could you give me a some basic info on Layouts. I read the tutorial on oracle, but I am still not 100% what I am actually doing. For some reason pack() ignores both panels atm. What could cause this? And which Layout would be best for this situation(just two panels under each other). – Haeri Nov 12 '14 at 00:24
  • - Yeah, `CardLayout` would want to size the container to the largest child component regardless of what's visible :P - Guess you'll have to do it manually – MadProgrammer Nov 12 '14 at 00:25
  • "remove" was a nice call! This works great! Now two problems remain: 1. Unless I set setPreferredSize on both panels, pack() ignores them. 2. I have no margin around the panels so it feels very clustered. Adding an emptyBorder doesn't seem to help. Is it because I use a BorderLayout? – Haeri Nov 12 '14 at 00:32
  • The size of the components should be determined by the layout manager, so there shouldn't be any need to call `setPreferredSize`. The "clustering" problem is hard to diagnose without know what layout managers are involved, but I would say the issue is with call `setPreferredSize` - see updated answer, no `setPreferredSize` calls there and it works just fine... – MadProgrammer Nov 12 '14 at 00:52

1 Answers1

5

There are probably a few ways you could do this, you could, for example, simply add and remove the bottom panel as needed. If I can, I'd like to avoid this, as it can make managing the layout more difficult.

Another solution is simply to make the component visible/invisible, but you need to find a layout manager which will actually treat a invisible component like it's not there (0x0) - and yes, I had issues with a couple while putting a test together...

Flip

import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Window;
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.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class FlipDialog {

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

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

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JButton flip;
        private JPanel bottom;

        public TestPane() {
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.insets = new Insets(8, 8, 8, 8);
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            JPanel top = new JPanel();
            flip = new JButton("+");
            top.add(flip);
            add(top, gbc);

            bottom = new JPanel();
            bottom.add(new JLabel("Boo"));
            bottom.setVisible(false);

            add(bottom, gbc);

            flip.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    bottom.setVisible(!bottom.isVisible());
                    if (bottom.isVisible()) {
                        flip.setText("-");
                    } else {
                        flip.setText("+");
                    }
                    revalidate();
                    Window window = SwingUtilities.windowForComponent(bottom);
                    window.pack();
                }
            });
        }

    }

}

It should be noted that the call to revalidate is probably irrelevant as you're packing the window anyway

Updated with layout example

ClosedOpended

import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractSpinnerModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class Test100 {

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

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

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new SearchPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class SearchPane extends JPanel {

        private ObjectsPane objectsPane;
        private AdvanceSettingsPane advanceSettingsPane;

        public SearchPane() {
            setBorder(new EmptyBorder(8, 8, 8, 8));
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.weightx = 1;
            gbc.weighty = 1;
            gbc.fill = GridBagConstraints.BOTH;

            objectsPane = new ObjectsPane();
            add(objectsPane, gbc);

            gbc.gridy++;
            gbc.weighty = 0;

            advanceSettingsPane = new AdvanceSettingsPane();
            advanceSettingsPane.setVisible(false);
            add(advanceSettingsPane, gbc);

            objectsPane.addExpandCollapseListener(new ChangeListener() {

                @Override
                public void stateChanged(ChangeEvent e) {
                    System.out.println(objectsPane.isExpanded());
                    advanceSettingsPane.setVisible(objectsPane.isExpanded());
                    Window window = SwingUtilities.windowForComponent(SearchPane.this);
                    window.pack();
                }
            });
        }

        public class ObjectsPane extends JPanel {

            private JSpinner findField;
            private JTextField replaceField;

            private JButton expandButton;
            private JButton replaceButton;
            private JButton replaceAllButton;

            private boolean expanded = false;

            public ObjectsPane() {
                setLayout(new GridBagLayout());

                findField = new JSpinner(new AbstractSpinnerModel() {

                    @Override
                    public Object getValue() {
                        return "";
                    }

                    @Override
                    public void setValue(Object value) {
                    }

                    @Override
                    public Object getNextValue() {
                        return "";
                    }

                    @Override
                    public Object getPreviousValue() {
                        return "";
                    }
                });
                replaceField = new JTextField(10);

                replaceButton = new JButton("Replace");
                replaceAllButton = new JButton("Replace All");
                expandButton = new JButton("+");

                GridBagConstraints gbc = new GridBagConstraints();
                gbc.insets = new Insets(4, 4, 4, 4);
                gbc.gridx = 0;
                gbc.gridy = 0;
                gbc.gridwidth = GridBagConstraints.REMAINDER;
                gbc.weightx = 1;
                gbc.anchor = GridBagConstraints.WEST;
                add(new JLabel("Objects found:"), gbc);

                gbc.gridx = 0;
                gbc.gridy = 1;
                gbc.gridwidth = 1;
                gbc.weightx = 0;
                add(new JLabel("Find:"), gbc);

                gbc.gridy = 2;
                add(new JLabel("Replace:"), gbc);

                gbc.gridx = 1;
                gbc.gridy = 1;
                gbc.weightx = 1;
                gbc.fill = GridBagConstraints.HORIZONTAL;
                gbc.gridwidth = GridBagConstraints.REMAINDER;
                add(findField, gbc);

                gbc.gridy = 2;
                add(replaceField, gbc);

                gbc.anchor = GridBagConstraints.WEST;
                gbc.gridwidth = 1;
                gbc.weightx = 0;
                gbc.gridx = 0;
                gbc.gridy = 3;
                gbc.fill = GridBagConstraints.NONE;
                add(expandButton, gbc);

                JPanel pnlButtons = new JPanel(new GridLayout(1, 2));
                pnlButtons.add(replaceButton);
                pnlButtons.add(replaceAllButton);

                gbc.gridx = 1;
                gbc.gridy = 3;
                gbc.fill = GridBagConstraints.HORIZONTAL;
                gbc.weightx = 1;
                gbc.gridwidth = GridBagConstraints.REMAINDER;
                add(pnlButtons, gbc);

                expandButton.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        expanded = !expanded;
                        if (expanded) {
                            expandButton.setText("-");
                        } else {
                            expandButton.setText("+");
                        }
                        fireStateChanged();
                    }
                });
            }

            public boolean isExpanded() {
                return expanded;
            }

            public void addExpandCollapseListener(ChangeListener listener) {
                listenerList.add(ChangeListener.class, listener);
            }

            public void removeExpandCollapseListener(ChangeListener listener) {
                listenerList.remove(ChangeListener.class, listener);
            }

            protected void fireStateChanged() {
                ChangeListener[] listeners = listenerList.getListeners(ChangeListener.class);
                if (listeners.length > 0) {

                    ChangeEvent evt = new ChangeEvent(this);
                    for (ChangeListener listener : listeners) {
                        listener.stateChanged(evt);
                    }

                }
            }

        }

        public class AdvanceSettingsPane extends JPanel {

            public AdvanceSettingsPane() {
                setBorder(new TitledBorder("Advance Settings"));
                setLayout(new GridBagLayout());
                GridBagConstraints gbc = new GridBagConstraints();
                gbc.gridx = 0;
//              gbc.gridy = 0;
                gbc.weightx = 1;
                gbc.anchor = GridBagConstraints.WEST;
                gbc.gridwidth = GridBagConstraints.REMAINDER;

                add(new JCheckBox("Live Update"), gbc);
                add(new JCheckBox("Word search"), gbc);
                add(new JCheckBox("Ignore Case"), gbc);
            }

        }

    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • This looks promising. I will mark this as soon as I manage to implement this into my code. Thank you very much! – Haeri Nov 12 '14 at 01:03
  • Ok. This works! Thank you. The problem with pack() was that my Panels had a null Layout. Now That I removed that everything works. Except my layout is completely broken. What do you think, which LayoutManager would be the best/easiest to recreate my null layout? – Haeri Nov 12 '14 at 08:26
  • I doubt a single layout manager will do the WHOLE job, but I would use a series of containers probably using `GridBagLayout`, but that's just me ;) – MadProgrammer Nov 12 '14 at 08:52
  • With GridBagLayout my code length has doubled and it looks like crap :( I'm really thinking about switching back to null-layout.. I know, I know, good programming habits and stuff.. but isn't it supposed to make the code easier and better..? – Haeri Nov 12 '14 at 13:01
  • You can switch back to null layout, but you loose all the benefits that the api offers (like pack and auto resizing). You could write your own resign code, but the reality is, you'd only be replicating what's already there. You'll also lose cross platform support, as the layouts take into consideration difference in rendering techniques used by different platforms, which change the preferred sizes of the components... – MadProgrammer Nov 12 '14 at 19:48
  • You could also check out http://stackoverflow.com/questions/26826365/why-setting-setpreferredsize-on-jframe-is-bad/26828479#26828479 for an example of what I'm talking about... – MadProgrammer Nov 12 '14 at 20:01
  • Ok. It is obvious that I need to get away from null Layouts. Which means I need to rewrite all my JDialogs. (Except the Search Dialog, because you, god sir are awesome!). Now I found a cool LayoutManager "GroupLayout" which works similar to null layout and which is very easy to use with my windowBuilder plugin for Eclipse. Now before I spend over 20h rewriting my stuff just to realize GroupLayout is as poop as null layout, I wanted to ask you first for your wise thoughts on the subject... So what do you think.. is it ok to use GroupLayout?? – Haeri Nov 14 '14 at 22:32
  • The reality is Group and Spring layouts were designed mostly for use by GUI editors, they weren't really designed for hand coding. You might consider looking into MigLayout which is suppose to be easier to use then GridBagLayout or more flexible – MadProgrammer Nov 14 '14 at 23:04