Align a JLabel with the text of a JCheckBox
Asked Answered
D

4

9

Is there a look-and-feel-independent way to align a component (e.g. a JLabel) horizontally with the text of a JCheckBox?

I am trying to use values from the UIDefaults to predict the location of the text relative to the top-left corner of the JCheckBox. I have found a combination that gives the right result for the Metal, Windows, Motif and Aqua Look-and-Feels:
Example: Metal (correctly-aligned)

But not in Nimbus:
Example: Nimbus (incorrectly-aligned)

Is there a utility method somewhere that will reliably give X,Y offsets for the text in all Look-and-Feels?


Code (note: to avoid any layout side-effects I used a null layout for this test):

import java.awt.Insets;

import javax.swing.JApplet;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.UIManager;
import javax.swing.border.Border;

public class AlignCheckBoxText extends JApplet {

    public AlignCheckBoxText() {
        setLayout(null);
        checkBox = new JCheckBox("Hello, World!");
        label = new JLabel("Hello, World!");
        add(checkBox);
        add(label);
    }

    @Override
    protected void validateTree() {
        checkBox.setLocation(0, 0);
        checkBox.setSize(checkBox.getPreferredSize());
        int labelX = UIManager.getIcon("CheckBox.icon").getIconWidth();
        Insets cbInsets = UIManager.getInsets("CheckBox.margin");
        if (cbInsets != null) labelX += cbInsets.left + cbInsets.right;
        Border cbBorder = UIManager.getBorder("CheckBox.border");
        if (cbBorder != null) {
            Insets borderInsets = cbBorder.getBorderInsets(checkBox);
            if (borderInsets != null) {
                labelX += borderInsets.left; 
            }
        }
        label.setLocation(labelX, checkBox.getHeight());
        label.setSize(label.getPreferredSize());
        super.validateTree();
    }

    private JCheckBox checkBox;
    private JLabel label;

}
Depressomotor answered 18/9, 2010 at 14:1 Comment(2)
In this case, it would be simplier to create a checkbox with no text, and a separate JLabel that contains the original checkbox's text.Mcclendon
@barjak, that will mess with focus display. Normally there is a border around the checkbox (icon and text) to show when it is focused. Also clicking on the text will no longer toggle the checkbox selection.Depressomotor
A
3

(To OP: please unaccept my earlier wrong answer, and I'll delete it. Sorry!)

You can use a checkbox that doesn't paint the checkbox for your labels. This works for all LAFs in Windows JDK6, including Nimbus. I don't have a mac so can't test Aqua.

class CheckBoxLabel extends JCheckBox
{
    public CheckBoxLabel(String string)
    {
        super(string);
    }

    public void updateUI()
    {
        setUI(new CheckBoxLabelUI());
    }
}

class CheckBoxLabelUI extends BasicCheckBoxUI
{
    public void installUI(JComponent c)
    {
        super.installUI(c);
        Icon i = super.getDefaultIcon();
        icon_ = new EmptyIcon(i.getIconWidth(), i.getIconHeight());
    }

    public Icon getDefaultIcon()
    {
        return icon_;
    }

    private Icon icon_;
}

class EmptyIcon implements Icon
{
    public EmptyIcon()
    {
        this(0, 0);
    }

    public EmptyIcon(int width, int height)
    {
        width_ = width;
        height_ = height;
    }

    public int getIconHeight()
    {
        return height_;
    }

    public int getIconWidth()
    {
        return width_;
    }

    public void paintIcon(Component c, Graphics g, int x, int y)
    {
    }

    private int width_;
    private int height_;
}
Amund answered 20/9, 2010 at 16:24 Comment(0)
A
2

You can right-align the label and set its width to be checkbox's width minus right border. It works for all LAFs with Windows JDK1.6.0_21, including Nimbus. I don't have a Mac so can't test Aqua.

Here's your code very slightly modified:

class AlignCheckBoxText extends JApplet {

    public AlignCheckBoxText() {
        setLayout(null);
        checkBox = new JCheckBox("Hello, World!");
        label = new JLabel("Hello, World!");
        add(checkBox);
        add(label);        
        label.setHorizontalAlignment(JLabel.TRAILING);
    }

    @Override
    protected void validateTree() {
        checkBox.setLocation(0, 0);
        checkBox.setSize(checkBox.getPreferredSize());
        int x = 0;
        Border cbBorder = UIManager.getBorder("CheckBox.border");
        if (cbBorder != null) {
            Insets borderInsets = cbBorder.getBorderInsets(checkBox);
            if (borderInsets != null) {
                x += borderInsets.right;
            }
        }
        label.setLocation(0, checkBox.getHeight());
        label.setSize(new Dimension(checkBox.getPreferredSize().width - x, label.getPreferredSize().height));
        super.validateTree();
    }

    private JCheckBox checkBox;
    private JLabel label;

}
Amund answered 18/9, 2010 at 21:49 Comment(2)
I wish I could delete or down vote my answer. It's really brain dead (no offense to OP, it's all my fault). It only works when checkbox and label have identical text. You could hack it by setting the real checkbox text after setting label size, but then you'd have to do that whenever label text changes. Also it fails for right-to-left locale. A real answer would probably need to delve into check box UI, which I'm not familiar with. Sorry.Amund
Even though this answer has drawbacks, I think this should stay. Don't delete it. Although maybe the OP could unaccept it.Weissman
M
1

This is not a complete response, but it might help you :

If you add checkBox.getIconTextGap() to the value of labelX, the alignment seems to be OK with Nimbus, but not OK with metal or GTK.

Mcclendon answered 18/9, 2010 at 16:25 Comment(2)
Interesting. checkBox.getIconTextGap gives a useful result (possibly a coincidence) in Nimbus but a non-useful result in other L&Fs. UIManager.getInt("CheckBox.textIconGap") always returns zero.Depressomotor
This was the solution I was going to suggest as well, although you should note that LAF's are not forced to use any of the UIManager default values. Also I do get a non zero value for the default LAF's. Check out the UIManager Defaults program (tips4java.wordpress.com/2008/10/09/uimanager-defaults). All the examples I've ever seen in the Swing tutorial override the init() method to create the GUI. Maybe the problem is with the method you are overriding and the LAF isn't installed yet? Try the normal way or try creating a JFrame for you demo to see if there is a difference.Shifra
W
1

If you use GroupLayout, the Netbeans GUI builder can align the text of a check box and a label on the next line.... sort of.

It appears to work well for Nimbus, but in the other LAF the label text is one pixel too far to the right. Looking at the generated code for the GroupLayout in the GUI builder, it makes a gap of 22x22 in front of the label. For Nimbus, 22 seems right, but for the other LAFs it appears to be 21.

The generated code looks like this.

GroupLayout layout = new GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHortizontalGroup(
  layout.createParallelGroup(Alignment.LEADING)
    .addGroup(layout.createSequentialGroup()
      .addContainerGap()
      .addGroup(layout.createParallelGroup(Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
          .addGap(22,22,22)
          .addComponent(label)
        )
        .addComponent(checkBox)
      )
    )
);

layout.setVerticalGroup(
  layout.createParallelGroup(Alignment.LEADING)
    .addGroup(layout.createSequentialGroup()
      .addContainerGap()
      .addComponent(checkBox)
      .addPreferredGap(ComponentPlacement.RELATED)
      .addComponent(label)
    )
);

This code would go in the constructor of the sample class after the assignments of label and checkBox. Also, remove the setLayout(null);

All of this being said, based on what I've seen using the GUI builder, I'd recommend the following for real:

  1. remove the Border cbBorder bit from the sample
  2. add the checkBox.getIconTextGap() to the labelX.
  3. If the LAF is nimbus, then add 4 to labelX. :)
Weissman answered 18/9, 2010 at 19:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.