0

I noticed if you try to use LEADING alignment in GroupLayout when one of the components is a button and the other is not a button, the components no longer line up.

If both components are buttons, you get results like this:

getContainerGap(button, EAST, container) -> 14
getContainerGap(another button, EAST, container) -> 14
getContainerGap(button, WEST, container) -> 14
getContainerGap(another button, WEST, container) -> 14
getContainerGap(button, SOUTH, container) -> 17
getPreferredGap(another button, button, RELATED, SOUTH, container) -> 4
getContainerGap(another button, SOUTH, container) -> 17

result of layout when both are buttons

If you get out a ruler, you can see that the left margin is exactly 20 pixels, so this result is correct.

If one of the components is not a button (usually I'm seeing this when I try to align a button to the left edge of a JScrollPane or similar), you get results like this:

getContainerGap(button, EAST, container) -> 14
getContainerGap(reference component, EAST, container) -> 20
getContainerGap(button, WEST, container) -> 14
getContainerGap(reference component, WEST, container) -> 20
getContainerGap(button, SOUTH, container) -> 17
getPreferredGap(reference component, button, RELATED, SOUTH, container) -> 7
getContainerGap(reference component, SOUTH, container) -> 20

result of layout when one component is not a button

Now if you get out a ruler, the left margin of the red border is exactly 20 pixels, but the button juts out an additional 6 pixels.

The getVisualMargin method exists to correct this, and you can see that getContainerGap(button, EAST, container) returns 14, which is compensating for the 6 pixels of extra margin outside the button.

If you have more types of component to lay out, the results get progressively worse and you end up with a jagged edge all the way down the layout, since only similar components will align with each other.

I'm wondering what is going on here. Even though the LayoutStyle is returning the right value for the container gap, it seems like GroupLayout is throwing away the value and using the value from the other component, which guarantees that the layout will be wrong.

Test program source:

import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.LayoutStyle;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;

public class ButtonVisualMarginTest implements Runnable {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new ButtonVisualMarginTest());
    }

    @Override
    public void run() {
        LayoutStyle.setInstance(new AquaLayoutStyle());

        JComponent reference = new JComponent() {};
        reference.setName("reference component");
        reference.setPreferredSize(new Dimension(200, 50));
        reference.setMaximumSize(new Dimension(200, 50));
        reference.setBorder(BorderFactory.createLineBorder(Color.RED));

//        JButton reference = new JButton("Another button");
//        reference.setName("another button");
//        reference.setFocusable(false);

        JButton button = new JButton("Text only");
        button.setName("button");
        button.setFocusable(false);

        JPanel container = new JPanel();
        container.setName("container");
        GroupLayout layout = new GroupLayout(container);
        layout.setAutoCreateGaps(true);
        layout.setAutoCreateContainerGaps(true);
        container.setLayout(layout);
        container.setBackground(new Color(192, 192, 255));
        container.setOpaque(true);

        layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addComponent(reference)
            .addComponent(button));

        layout.setVerticalGroup(layout.createSequentialGroup()
            .addComponent(reference)
            .addComponent(button));

        JFrame frame = new JFrame("Test");
        frame.setLayout(new BorderLayout());
        frame.add(container, BorderLayout.CENTER);
        frame.setSize(400, 300);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setVisible(true);

    }
}

The slightly modified version of AquaLayoutStyle I'm trying to work with is here (it was too big to include.)

Because people might be curious, here is the result of not using AquaLayoutStyle:

result of not using AquaLayoutStyle

And here is the layout you get when using Xcode to lay out a button next to a scrollable component (this is what we should be aiming for if we want Java applications to look native.)

native layout of similar components

Hakanai
  • 12,010
  • 10
  • 62
  • 132
  • `LayoutStyle.setInstance(new AquaLayoutStyle());` Does it work *without* the aqua style? – Andrew Thompson Feb 07 '14 at 12:42
  • Without it, it's worse. You don't get enough spacing between the container and the components, *and* the components still don't line up (the very point of `AquaLayoutStyle` is to fix the layout.) – Hakanai Feb 07 '14 at 12:46
  • I added some more screenshots to show what the default `LayoutStyle` looks like and also what native apps look like. – Hakanai Feb 07 '14 at 12:53
  • @AndrewThompson I can't even figure out what you think you're trying to say. The whole point of GroupLayout is to provide a layout manager where when you say things should line up to the left, they will line up to the left. Clearly they are *not* lining up to the left, so I'm asking how to fix that. PLAFs *are* responsible for providing a working `LayoutStyle`, so I don't understand why you seem to think they're not responsible for layout-related issues. – Hakanai Feb 13 '14 at 12:04

0 Answers0