11

I am using the following code to present an error dialog in Java Swing:

JOptionPane.showMessageDialog(null, "Arquivo de imagem não encontrado. Por gentileza, altere o caminho do arquivo.", "Erro",  JOptionPane.ERROR_MESSAGE);

using Windows 10 default look and feel through:

UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

But the icon looks cropped, like this:

Dialog with cropped icon

Any ideas on how to solve that?

Here is the SSCCE:

import javax.swing.JOptionPane;
import javax.swing.UIManager;

public class SSCCE {
    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            JOptionPane.showMessageDialog(null, "Error message", "Error",  JOptionPane.ERROR_MESSAGE);
        } catch (Exception e){
            e.printStackTrace();
        }
    }

}
TylerH
  • 20,799
  • 66
  • 75
  • 101
dannyxyz22
  • 978
  • 9
  • 18

2 Answers2

6

Looks like Oracle has finally taken notice of this and will fix it in JDK 9, but for the time being here's a qiuck, hacky and Windows dependent solution for those not willing to provide custom icons.

Insert this code after your call to UIManager.setLookAndFeel():

try
{
    String[][] icons =
    {
        {"OptionPane.errorIcon", "65581"},
        {"OptionPane.warningIcon", "65577"},
        {"OptionPane.questionIcon", "65579"},
        {"OptionPane.informationIcon", "65583"}
    };
    //obtain a method for creating proper icons
    Method getIconBits = Class.forName("sun.awt.shell.Win32ShellFolder2").getDeclaredMethod("getIconBits", new Class[]{long.class, int.class});
    getIconBits.setAccessible(true);
    //calculate scaling factor
    double dpiScalingFactor = Toolkit.getDefaultToolkit().getScreenResolution() / 96.0;
    int icon32Size = (dpiScalingFactor == 1)?(32):((dpiScalingFactor == 1.25)?(40):((dpiScalingFactor == 1.5)?(45):((int) (32 * dpiScalingFactor))));
    Object[] arguments = {null, icon32Size};
    for (String[] s:icons)
    {
        if (UIManager.get(s[0]) instanceof ImageIcon)
        {
            arguments[0] = Long.valueOf(s[1]);
            //this method is static, so the first argument can be null
            int[] iconBits = (int[]) getIconBits.invoke(null, arguments);
            if (iconBits != null)
            {
                //create an image from the obtained array
                BufferedImage img = new BufferedImage(icon32Size, icon32Size, BufferedImage.TYPE_INT_ARGB);
                img.setRGB(0, 0, icon32Size, icon32Size, iconBits, 0, icon32Size);
                ImageIcon newIcon = new ImageIcon(img);
                //override previous icon with the new one
                UIManager.put(s[0], newIcon);
            }
        }
    }
}
catch (Exception e)
{
    e.printStackTrace();
}

Why this works

On Windows, icons provided to applications are already scaled by the current DPI setting (96 = no scaling, 120 = +25%, 144 = +50%). Unfortunatelly, Java always assumes that icons are of size either 16x16 or 32x32, which is not the case. The code above utilizes a native method sun.awt.shell.Win32ShellFolder2.getIconBits() used by Java to obtain OS icons, but provides appropriate sizes. This approach may become unusable in the future, as Win32ShellFolder2 is not part of the API and may be modified or removed entirely, but so far this is the only approach preserving the native icons.

mkJ720
  • 71
  • 1
  • 2
3

mkJ720's answer works well, but the icon ID's are off. Here are the corrected ones:

String[][] icons = {
    {"OptionPane.warningIcon",     "65581"},
    {"OptionPane.questionIcon",    "65583"},
    {"OptionPane.errorIcon",       "65585"},
    {"OptionPane.informationIcon", "65587"}
};
  • In case someone is still looking for a solution to this problem, this set of icon IDs seems to be correct on my machine, but icon is different on tester's machine for exactly the same message. Mine is a mini-desktop, hers is a laptop, both windows10, the system update is in full-control by IT department. – H.Sheng Mar 29 '21 at 11:53
  • Wonder how do you get the number, e.g. "65581", "65583", etc., for each icon. Tried on a couple more laptops; on some laptops, the Error message has a red X icon, and the Confirmation message has a blue ? icon; on the other laptops, the Error message has the blue ? icon, and the Confirmation message has a yellow ! icon. DIFFERENT result on DIFFERENT machine! – H.Sheng Mar 29 '21 at 12:26