mezzodrinker
mezzodrinker

Reputation: 988

JOptionPane no button text with custom LNF

I tried styling a custom-made Dialog which allows the user to create a new folder. Until I loaded my custom Look and Feel, everything looked just fine:

Dialog without custom Look and Feel

Now I tried setting the Look and Feel and the texts of the buttons disappeared.

Dialog with custom Look and Feel

I set up an SSCCE which demonstrates this behaviour. Please note that the LNF class does nothing except for loading all values from UIManager.getDefaults(). If this helps, I'm using Debian 8 (jessie) with Gnome 3.18.1 and the global dark theme toggled on.

Why does this happen and how can I fix the disappearing of those button texts?

Upvotes: 1

Views: 360

Answers (2)

aterai
aterai

Reputation: 9833

Another common way is to use the UIDefaults#addResourceBundle(...) method:

//@see javax/swing/plaf/basic/BasicLookAndFeel.java
@Override
public UIDefaults getDefaults() {
  UIDefaults defaults = new UIDefaults();
  defaults.putAll(UIManager.getDefaults());
  //defaults.addResourceBundle("com.example.swing.plaf.light.resources.light");
  defaults.addResourceBundle("com.sun.swing.internal.plaf.basic.resources.basic");
  return defaults;
}

Test2.java

import java.awt.*;
import java.io.File;
import java.util.*;
import javax.swing.*;
import javax.swing.GroupLayout.Alignment;
import javax.swing.GroupLayout.SequentialGroup;

public class Test2 {
    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(new LightLookAndFeel());
            DialogFactory.showCreateFolderDialog(null, new File("."));
        } catch (UnsupportedLookAndFeelException e) {
            e.printStackTrace();
        }
    }
}

class DialogFactory {
    private DialogFactory() {}

    public static void showCreateFolderDialog(Component component, File parent) {
        JPanel panel = new JPanel();
        JLabel message = new JLabel("Please enter a folder name.");
        JLabel label = new JLabel("Folder name:");
        final JLabel errorMessage = new JLabel();
        JTextField folderName = new JTextField();
        GroupLayout layout = new GroupLayout(panel);
        layout.setHonorsVisibility(false);
        layout.setAutoCreateContainerGaps(true);
        layout.setAutoCreateGaps(true);
        panel.setLayout(layout);
        errorMessage.setVisible(false);

        SequentialGroup hGroup = layout.createSequentialGroup();
        SequentialGroup input = layout.createSequentialGroup().addComponent(label).addComponent(folderName);
        hGroup.addGroup(layout.createParallelGroup().addComponent(message).addGroup(input).addComponent(errorMessage));
        layout.setHorizontalGroup(hGroup);

        SequentialGroup vGroup = layout.createSequentialGroup();
        vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(message));
        vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(label).addComponent(folderName));
        vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(errorMessage));
        layout.setVerticalGroup(vGroup);

        final JOptionPane optionPane = new JOptionPane(panel, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
        final JDialog dialog = new JDialog(JOptionPane.getFrameForComponent(component), "Create new folder", true);
        dialog.setLocationRelativeTo(component);
        dialog.setContentPane(optionPane);
        dialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
        optionPane.addPropertyChangeListener(event -> {
            String property = event.getPropertyName();

            if (dialog.isVisible() && event.getSource() == optionPane && property.equals(JOptionPane.VALUE_PROPERTY)) {
                if (folderName.getText().isEmpty() && (int) event.getNewValue() != JOptionPane.CANCEL_OPTION) {
                    errorMessage.setText("<html><body><b style='color: red'>Please enter a valid folder name!</b></body></html>");
                    errorMessage.setVisible(true);
                    dialog.pack();
                } else {
                    dialog.setVisible(false);
                }
            }
        });
        dialog.pack();
        dialog.setVisible(true);

        int response = ((Integer) optionPane.getValue()).intValue();
        if (response != JOptionPane.OK_OPTION) return;
        File newFolder = new File(parent, folderName.getText());
        newFolder.mkdirs();
    }
}

class LightLookAndFeel extends LookAndFeel {
    private final UIDefaults defaults = new UIDefaults();

    public LightLookAndFeel() {
        defaults.putAll(UIManager.getDefaults());
    }

    @Override
    public void initialize() {
    }

    @Override
    public String getName() {
        return "Light";
    }

    @Override
    public String getID() {
        return getClass().getName();
    }

    @Override
    public String getDescription() {
        return "Light Look and Feel";
    }

    @Override
    public boolean isNativeLookAndFeel() {
        return false;
    }

    @Override
    public boolean isSupportedLookAndFeel() {
        return true;
    }

    @Override
    public UIDefaults getDefaults() {
        //defaults.addResourceBundle("com.example.swing.plaf.light.resources.light");
        defaults.addResourceBundle("com.sun.swing.internal.plaf.basic.resources.basic");
        return defaults;
    }
}

// package com.example.swing.plaf.light.resources;
// import java.util.*;
//
// public class light extends ListResourceBundle {
//     @Override protected final Object[][] getContents() {
//         return new Object[][] {
//             //...
//             { "OptionPane.cancelButtonMnemonic", "0" },
//             { "OptionPane.cancelButtonText", "Cancel" },
//             { "OptionPane.inputDialogTitle", "Input" },
//             { "OptionPane.messageDialogTitle", "Message" },
//             { "OptionPane.noButtonMnemonic", "78" },
//             { "OptionPane.noButtonText", "No" },
//             { "OptionPane.okButtonMnemonic", "0" },
//             { "OptionPane.okButtonText", "OKkkk" },
//             { "OptionPane.titleText", "Select an Option" },
//             { "OptionPane.yesButtonMnemonic", "89" },
//             { "OptionPane.yesButtonText", "Yessss" },
//             //...
//         };
//     }
// }
// public class light_de extends ListResourceBundle { //...
// public class light_es extends ListResourceBundle { //...
// public class light_fr extends ListResourceBundle { //...
// public class light_it extends ListResourceBundle { //...
// public class light_ja extends ListResourceBundle { //...
// public class light_ko extends ListResourceBundle { //...
// public class light_pt_BR extends ListResourceBundle { //...
// public class light_sv extends ListResourceBundle { //...
// public class light_zh_CN extends ListResourceBundle { //...
// public class light_zh_HK extends ListResourceBundle { //...
// public class light_zh_TW extends ListResourceBundle { //...

Upvotes: 3

mezzodrinker
mezzodrinker

Reputation: 988

The values retrieved by UIManager.getDefaults() do not include the texts for JOptionPane buttons whose properties are OptionPane.cancelButtonText and OptionPane.okButtonText respectively. This causes the buttons to be displayed without any text, causing them to be as small as in the screenshot provided in the question. By setting OptionPane.cancelButtonText and OptionPane.okButtonText to the desired values, the JOptionPane buttons will display the set text. This solution may also apply for the yes/no buttons.

Upvotes: 2

Related Questions