Search code examples
javaswingjcheckbox

Align a JLabel with the text of a JCheckBox


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;

}

Solution

  • (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_;
    }