Martin Cmar
Martin Cmar

Reputation: 93

JScrollPane and GridBagLayout problems while creating Inspector-like thing

I am creating something like inspector panel for my program, so I can easily change some variables at runtime.

When you run the code bellow, you should see something like this: inspector screenshot

There are several things that bothers me and I still cannot make them to work properly.

  1. Items are verticaly aligned to the center... I have tried setting anchor to the North but it remains the same. In the example code bellow, there is one line commented out that can fix this: inspector.finish() but the solution seems kinda hacky to me. It works by adding empty JPanel as last item on the panel, acting as some kind of vertical glue to expand the lower area and push the components up. I don't like this, because with this solution I no longer can add more items to the inspector later at runtime.
  2. If you add more items to the inspector (you can do this by changing n variable that fills the inspector with some test data) the scroll bar will not show up, and all the lower items are out of screen even it's all wrapped inside JScrollPane... I have tried several hacks to fix this but none of them works correctly. One of them was setting preferredSize of JPanel to some hard-coded dimensions but I don't like this because I don't know exact size of the panel.
  3. When you push some of the spinner buttons and then click on the combobox (titled as "choose me"), the options are hidden behind the button bellow. This looks like some kind of z-ordering issue. Maybe this is bug in swing, dunno.
  4. When you resizing the window vertically to smaller size, the items on the top will begin to shrink instead of staying the same size. Is there any option to set them constant height at all times?

Maybe I am using the wrong layout manager, but I don't know which other one to choose. I was thinking about simple grid layout, but this does not allow me to do things like having some rows with one item and others with multiple items in such a way that they will always use the whole width "capacity" of the row.

example code:

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

public class Test {

    // Setup test scenario
    public Test() {

        // Create window
        JFrame f = new JFrame();
        f.setLayout(new BorderLayout());
        f.setSize(800, 600);
        f.setTitle("Inspector");
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        // Create panel which will be used for the inspector
        JPanel p = new JPanel();
        p.setPreferredSize(new Dimension(200, 0));

        // Encapsulate it to the scroll panel so it can grow vertically
        JScrollPane sp = new JScrollPane();
        sp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        sp.setViewportView(p);

        // Create the inspector inside panel p
        Inspector inspector = new Inspector(p);

        // Fill the inspector with some test data
        int n = 3;
        for (int i = 0; i < n; ++i) {
            inspector.addTitle("Title");
            inspector.addButton("push me");
            inspector.addCheckBox("check me");
            inspector.addSpinner("Spin me");
            inspector.addTextField("Edit me", "here");
            inspector.addComboBox("Choose one", new String[]{"A", "B", "C"});
            inspector.addSeparator();
        }
        //inspector.finish();

        // The inspector will be on the right side of the window
        f.getContentPane().add(sp, BorderLayout.LINE_END);

        // Show the window
        f.setVisible(true);
    }

    // Main method
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Test();
            }
        });
    }

    // Inspector class itself
    private class Inspector {

        private final JPanel panel;
        private final GridBagConstraints constraints;
        private int itemsCount = 0;

        public Inspector(JPanel p) {
            panel = p;
            panel.setLayout(new GridBagLayout());

            constraints = new GridBagConstraints();
            constraints.fill = GridBagConstraints.HORIZONTAL;
            constraints.weightx = 0.5;
        }

        // Adds component which will span across whole row
        private void addComponent(Component component) {
            constraints.gridx = 0;
            constraints.gridwidth = 2;
            constraints.gridy = itemsCount;

            panel.add(component, constraints);

            itemsCount++;
        }

        // Adds descriptive label on the left and component on the right side of the row
        private void addNamedComponent(String description, Component component) {
            constraints.gridx = 0;
            constraints.gridy = itemsCount;
            constraints.gridwidth = 1;
            panel.add(new JLabel(description), constraints);

            constraints.gridx = 1;
            constraints.gridy = itemsCount;
            constraints.gridwidth = 1;
            panel.add(component, constraints);

            itemsCount++;
        }

        public void addSeparator() {
            // (little hacky)
            addComponent(new JPanel());
        }

        public void addTitle(String title) {
            addComponent(new JLabel(title));
        }

        public void addButton(String actionName) {
            addComponent(new Button(actionName));
        }

        public void addCheckBox(String description) {
            addNamedComponent(description, new JCheckBox());
        }

        public void addSpinner(String description) {
            addNamedComponent(description, new JSpinner());
        }

        public void addTextField(String description, String text) {
            addNamedComponent(description, new JTextField(text));
        }

        public void addComboBox(String description, String[] options) {
            JComboBox<String> comboBox = new JComboBox<>();
            ComboBoxModel<String> model = new DefaultComboBoxModel<>(options);
            comboBox.setModel(model);

            addNamedComponent(description, comboBox);
        }

        public void finish() {
            constraints.gridx = 0;
            constraints.gridy = itemsCount;
            constraints.gridwidth = 2;
            constraints.weighty = 1.0;

            panel.add(new JPanel(), constraints);
        }
    }
}

Upvotes: 0

Views: 117

Answers (1)

Arthur
Arthur

Reputation: 1246

The Reason that you can't scroll up or down is because you are setting the preferred size of sp to 200x0. You need to remove this line.

p.setPreferredSize(new Dimension(200, 0));

As for the issue with everything being centered instead of at the top. I would prefer to leave p with the default FlowLayout and give each "section" it's own panel, and the make that panel a GridBagLayout. By doing this you probably will not need addSeparator() anymore.

public class Test {

    // Setup test scenario
    public Test() {

        // Create window
        JFrame f = new JFrame();
        f.setLayout(new BorderLayout());
        f.setSize(800, 600);
        f.setTitle("Inspector");
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        // Create panel which will be used for the inspector
        JPanel p = new JPanel();

        // Encapsulate it to the scroll panel so it can grow vertically
        JScrollPane sp = new JScrollPane();
        sp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        sp.setViewportView(p);

        // Create the inspector inside panel p
        Inspector inspector = new Inspector(p);

        // Fill the inspector with some test data
        int n = 2;
        for (int i = 0; i < n; ++i) {
            inspector.addTitle("Title");
            inspector.addButton("push me");
            inspector.addCheckBox("check me");
            inspector.addSpinner("Spin me");
            inspector.addTextField("Edit me", "here");
            inspector.addComboBox("Choose one", new String[]{"A", "B", "C"});
        }

        // The inspector will be on the right side of the window
        f.getContentPane().add(sp, BorderLayout.LINE_END);

        // Show the window
        f.setVisible(true);
    }

    // Main method
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Test();
            }
        });
    }

    // Inspector class itself
    private class Inspector {

        private final JPanel panel;
        private final GridBagConstraints constraints;
        private int itemsCount = 0;

        public Inspector(JPanel p) {
            panel = new JPanel();
            p.add(panel);
            panel.setLayout(new GridBagLayout());

            constraints = new GridBagConstraints();
            constraints.fill = GridBagConstraints.HORIZONTAL;
            constraints.weightx = 1.0;
        }

        // Adds component which will span across whole row
        private void addComponent(Component component) {
            constraints.gridx = 0;
            constraints.gridwidth = 2;
            constraints.gridy = itemsCount;

            panel.add(component, constraints);

            itemsCount++;
        }

        // Adds descriptive label on the left and component on the right side of the row
        private void addNamedComponent(String description, Component component) {
            constraints.gridx = 0;
            constraints.gridy = itemsCount;
            constraints.gridwidth = 1;
            panel.add(new JLabel(description), constraints);

            constraints.gridx = 1;
            constraints.gridy = itemsCount;
            constraints.gridwidth = 1;
            panel.add(component, constraints);

            itemsCount++;
        }

        public void addSeparator() {
            // (little hacky)
            addComponent(new JPanel());
        }

        public void addTitle(String title) {
            addComponent(new JLabel(title));
        }

        public void addButton(String actionName) {
            addComponent(new Button(actionName));
        }

        public void addCheckBox(String description) {
            addNamedComponent(description, new JCheckBox());
        }

        public void addSpinner(String description) {
            addNamedComponent(description, new JSpinner());
        }

        public void addTextField(String description, String text) {
            addNamedComponent(description, new JTextField(text));
        }

        public void addComboBox(String description, String[] options) {
            JComboBox<String> comboBox = new JComboBox<>();
            ComboBoxModel<String> model = new DefaultComboBoxModel<>(options);
            comboBox.setModel(model);

            addNamedComponent(description, comboBox);
        }

        public void finish() {
            constraints.gridx = 0;
            constraints.gridy = itemsCount;
            constraints.gridwidth = 2;
            constraints.weighty = 1.0;

            panel.add(new JPanel(), constraints);
        }
    }
}

Upvotes: 1

Related Questions