Search code examples
javaswinglook-and-feel

How to use the built-in PLAF with custom components?


I'm creating a custom button, and I'm having trouble getting it to look right on most of the built-in PLAFs.

Here's my code

public MyButton(String text, Icon icon) {
    if (icon == null) icon = createDefaultIcon();

    mainButton = new JButton(text);
    popupButton = new JButton(icon);

    removeBorder(mainButton);
    removeBorder(popupButton);

    setModel(new DefaultButtonModel());
    setBorder(UIManager.getBorder("Button.border"));

    int popupButtonWidth = popupButton.getPreferredSize().width;
    int popupButtonHeight = mainButton.getPreferredSize().height;
    Dimension popupButtonSize = new Dimension(popupButtonWidth, popupButtonHeight);

    popupButton.setMinimumSize(popupButtonSize);
    popupButton.setPreferredSize(popupButtonSize);
    popupButton.setMaximumSize(popupButtonSize);

    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
    add(mainButton);
    add(new JSeparator(VERTICAL));
    add(popupButton);
}

private void removeBorder(JButton button) {
    Border border = button.getBorder();

    if (border instanceof CompoundBorder) {
         button.setBorder(((CompoundBorder) border).getInsideBorder());
    } else {
        button.setBorder(BorderFactory.createEmptyBorder());
    }
}

Here is how the button looks in the PLAFs installed on my computer

Metal

My Custom Button Metal PLAF Example

Nimbus

My Custom Button Nimbus PLAF Example

CDE/Motif

My Custom Button CDE/Motif PLAF Example

Mac OS X

My Custom Button Mac OS PLAF Example

CDE/Motif is the only one that works properly. I looked at the source for some of the ButtonUIs, and it seems they can ignore the background color and the borders. Unfortunately the background color and the borders are what I need to set. How do I get my custom button to support the built-in PLAFs correctly?

Edit: As requested, here's the code I used to produce the images

public class MyButtonDemo implements Runnable {

    public void run() {
        // Change the array index to get a different PLAF
        try {
            UIManager.setLookAndFeel(UIManager.getInstalledLookAndFeels()[0].getClassName());
        } catch (Exception ignored) { }

        JFrame frame = new JFrame();
        frame.getContentPane().setLayout(new FlowLayout());
        frame.getContentPane().add(new MyButton("My Button", null);
        frame.getContentPane().add(new JButton("Normal Button"));
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new MyButtonDemo());
    }

}

Solution

  • Not sure what I did, but it's working now for all but Nimbus. Here's the code

    public MyButton(String text, Icon icon) {
        arrowIcon = createDefaultArrowIcon();
        mainButton = new JButton(text, icon);
        popupButton = new JButton(arrowIcon);
    
        mainButton.setBorder(BorderFactory.createEmptyBorder());
        popupButton.setBorder(BorderFactory.createEmptyBorder());
    
        setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
        add(mainButton);
        add(new JSeparator(VERTICAL));
        add(popupButton);
    
        setModel(new DefaultButtonModel());
        init(null, null);
    }
    
    @Override
    public void updateUI() {
        setUI((ButtonUI)UIManager.getUI(this));
    }
    
    @Override
    public String getUIClassID() {
        return "ButtonUI";
    }