Bad Swing UI scaling on high resolution (MS Surface)

3.7k views Asked by At

I'm currently working on a little Java application, that involves Swing GUIs. On my development PC everything looks fine but when I run it on my MS Surface, some icons seem to be too large for the components (or the components too small for the icons).

Here's what I mean:

scaling issue

Google research has lead me to conclude that this is due to Surface's high resolution and Win8's zooming to let some items appear a little larger. So I reset that zoom to 100% and it actually fixed the bad scaling.

Unfortunately, this doesn't really fix my problem. Everything is far too small without the zoom, so I'd rather not disable it. But is there any clever way to solve this? Can I just "unscale" my program's or Java's icons? Ideally, I would even like upscale the entire frame, because everything is rather small.

Edit: obviously, I've also tried just resizing the actual JFrame but it has no effect on the dialog size. I'm calling the dialog by

JOptionPane.showMessageDialog(frame, msg, "Information", JOptionPane.INFORMATION_MESSAGE);

by the way.

3

There are 3 answers

2
rosa On

Here is a nasty, hacky, quick-fix solution which will stop the nasty cropping by resizing Swing's own icons to 80%.

In main, add this:

String[] iconOpts = {"OptionPane.errorIcon", 
  "OptionPane.informationIcon", 
  "OptionPane.warningIcon", 
  "OptionPane.questionIcon"};
for (String key : iconOpts) {
  ImageIcon icon = (ImageIcon) UIManager.get(key);
  Image img = icon.getImage();
  BufferedImage bi = new BufferedImage(
          img.getWidth(null), img.getHeight(null), 
          BufferedImage.TYPE_INT_ARGB);
  java.awt.Graphics g = bi.createGraphics();
  g.drawImage(img, 0, 0, 
          (int) (img.getWidth(null) * 0.8), 
          (int) (img.getHeight(null) * 0.8), null);
  ImageIcon newIcon = new ImageIcon(bi);
  UIManager.put(key, newIcon);
}

You might want to first check whether this is actually required - Windows 8/10 defaults to 125% but some people will switch it back to 100%. I haven't found an elegant way to do this, but something along these lines will give you an idea:

java.awt.Font font = (java.awt.Font) UIManager.get("Label.font");
if (font.getSize() != 11) {
    //resize icons in here
}
0
gmetax On

Most Swing Look & Feels don't support high DPI at all, not even Nimbus even though it's supposed to be scalable. I found some old blog posts saying that Nimbus might eventually offer high DPI scaling, but apparently that never happened.

The one exception is System LAF but its default font is ~10% smaller than the actual system font size, at all DPI settings. Moreover, System must be selected explicitly as described here: http://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html

There's no single scaling factor that you could set in Swing, either. The specific LAF has to provide code to handle scaling. So the best you can do is select System and hope it's good enough.

However, JavaFX does correctly and automatically scale all the way up to 150% on my system. If at all possible, I suggest you use JavaFX to build your GUI.

edit: I made a couple small test programs and took comparison screenshots for various GUI frameworks, Swing themes, and DPI settings. This might be informative for people reading this question: http://kynosarges.org/GuiDpiScaling.html

0
Crigges On

Based on rosa's answer i created a shorter less verbose variant:

String[] iconOpts = {"OptionPane.errorIcon", 
    "OptionPane.informationIcon", 
    "OptionPane.warningIcon", 
    "OptionPane.questionIcon"};
for (String key : iconOpts) {
    ImageIcon icon = (ImageIcon) UIManager.get(key);
    Image img = icon.getImage();
    ImageIcon newIcon = new ImageIcon(img.getScaledInstance(img.getWidth(null), 
        img.getHeight(null), 0));
    UIManager.put(key, newIcon);
}  

P.S.: Due to the reputation system i was not able to comment to rosa's post.

Edit: It seems to me like Java 10 has included a fix for highdpi scaling. So maybe check the java version first before using any of the solutions here.