HiDPI support in Java Swing for multiple viewing

I want to add Hi-DPI support to some Swing applications, but I could not find a solution sufficient for my needs. I need to maintain a multiple look, so the situation seems more complicated than the other messages I found (which usually suggest "tailor the size of your user interface to fit your font size").

In some experiments, it was discovered that it UIManagercontains many indicators that you can customize so that you can get started with the Hi-DPI application. (The UIManager-Defaults utility was invaluable for learning about these!) I found that L & Fs work completely differently for each other:

  • Windows L & F gives you a good (not perfect) default font size, and built-in icons (such as check boxes and window icons) are sized accordingly, but many other metrics are still out of order.

  • In Metal, you can update fonts UIManagerindividually. With a little work you can scale the built-in IconUIResourceto fit.

  • In Nimbus, you can simply update one default font, and other fonts fall into place ... but it's outside of me how to scale the built-in icons and have combined fields, radio buttons (etc.), rendering successfully!

The feeling I get from the game is that it should be possible to create a list of specific settings for each L&F-specific independently. This will include the ability to configure the default values for Font, Icon, Integerand Dimensions.

Has anyone come up with a good solution?

Can someone share a specific list that UIDefaultsneeds a setting for standard L & Fs?

I would be pleased with a solution that would always support Metal and Windows.

, Swing. , . (, , !) , (, setPreferredSize .. , Mulitple L & Fs, ). , , , .

, JDK-9 promises full -DPI, - , 2017.

+4
2
  • , , , , , , ,

  • , ( Win, LI/(U) NIX OSX)

    • NullLayout, , UIManager ( , ) getPreferredSize 21k x 48k, , LayoutManager , , , GridBagLayout, / 4k sceens, , AWT/Swing GUI 2k,

    • 2k/4k UIManager (, AFAIK Nimbus L & F , XML )

      • paintIcon ( AWT/Swing )
    • " " , 4: 3 β†’ (HD_screens) β†’ fullhd screen β†’ fullhs wide sceen, 2 , , Nimbus L & F, ,

      • ( , , ),
      • , , FullHD (16: 9) FullHd (21: 9).
      • , , ( , , - GPU, - -)

      • GUI , , getPreferredSize () , LayoutManager hardpoded getPreferredSize ,

    • , ..... ..

+1

, .

, , " " "" "". , Swing/L & F.

, , , , , , .

, ui- :

public interface Tweaker {
  void initialTweaks();
  Font modifyFont(Object key, Font original);
  Icon modifyIcon(Object key, Icon original);
  Integer modifyInteger(Object key, Integer original);
}

:

public void scaleUiDefaults() {
  float dpiScale = Toolkit.getDefaultToolkit().getScreenResolution() / 96f;
  Tweaker delegate = createTweakerForCurrentLook(dpiScale);
  tweakUiDefaults(delegate, dpiScale);
}

private Tweaker createTweakerForCurrentLook(float dpiScaling) {
  String testString = UIManager.getLookAndFeel().getName().toLowerCase();
  if (testString.contains("windows")) return new WindowsTweaker(dpiScaling);
  if (testString.contains("nimbus")) return new NimbusTweaker(dpiScaling);
  return new BasicTweaker(dpiScaling);
}

private void tweakUiDefaults(Tweaker delegate, float multiplier) {
  UIDefaults defaults = UIManager.getLookAndFeelDefaults();

  delegate.initialTweaks();

  for (Object key: Collections.list(defaults.keys())) {
    Object original = defaults.get(key);
    Object newValue = getUpdatedValue(delegate, key, original);
    if (newValue != null && newValue != original) {
      defaults.put(key, newValue);
    }
  }
}

private Object getUpdatedValue(Tweaker delegate, Object key, Object original) {
  if (original instanceof Font)    return delegate.modifyFont(key, (Font) original);
  if (original instanceof Icon)    return delegate.modifyIcon(key, (Icon) original);
  if (original instanceof Integer) return delegate.modifyInteger(key, (Integer) original);
  return null;
}

, , -, L & Fs . , , :

public class BasicTweaker {
  protected final float scaleFactor;
  protected final UIDefaults uiDefaults = UIManager.getLookAndFeelDefaults();

  public BasicTweaker(float scaleFactor) {
    this.scaleFactor = scaleFactor;
  }

  public void initialTweaks() {}

  public Font modifyFont(Object key, Font original) {

    // Ignores title & accelerator fonts (for example)
    if (original instanceof FontUIResource && key.toString().endsWith(".font")) {
        return newScaledFontUIResource(original, scaleFactor);
    }
    return original;
  }

  protected static FontUIResource newScaledFontUIResource(Font original, float scale) {
    int newSize = Math.round(original.getSize() * scale);
    return new FontUIResource(original.getName(), original.getStyle(), newSize);
  }

  public Icon modifyIcon(Object key, Icon original) {
    return new IconUIResource(new ScaledIcon(original, scaleFactor));
  }

  public Integer modifyInteger(Object key, Integer original) {
    if (!endsWithOneOf(lower(key), LOWER_SUFFIXES_FOR_SCALED_INTEGERS)) {
      return original;
    }
    return (int) (original * scaleFactor);
  }

  private boolean endsWithOneOf(String text, String[] suffixes) {
    return Arrays.stream(suffixes).anyMatch(suffix -> text.endsWith(suffix));
  }

  private String lower(Object key) {
    return (key instanceof String) ? ((String) key).toLowerCase() : "";
  }

  private static final String[] LOWER_SUFFIXES_FOR_SCALED_INTEGERS = 
    new String[] { "width", "height", "indent", "size", "gap" };
}

class ScaledIcon, , , - ImageIcon(image).paintIcon .

BasicTweaker L & Fs...

:

public class WindowsTweaker extends BasicTweaker {

  public WindowsTweaker(float scaleFactor) {

    // Windows already scales fonts, scrollbar sizes (etc) according to the system DPI settings.
    // This lets us adjust to the REQUESTED scale factor, relative to the CURRENT scale factor
    super(scaleFactor / getCurrentScaling());
  }

  private static float getCurrentScaling() {
    int dpi = Toolkit.getDefaultToolkit().getScreenResolution();
    return dpi / 96f;
  }

  public Font modifyFont(Object key, Font original) {
    return super.modifyFont(key, original);
  }

  public Icon modifyIcon(Object key, Icon original) {
    return original;
  }
}

Nimbus:

public class NimbusTweaker extends BasicTweaker {

  public NimbusTweaker(float scaleFactor) {
    super(scaleFactor);
  }

  public void initialTweaks() {
    Font font = uiDefaults.getFont("defaultFont");
    if (font != null) {
      uiDefaults.put("defaultFont", new FontUIResource(
          font.getName(), font.getStyle(), Math.round(font.getSize() * scaleFactor)));
    }
  }

  // Setting "defaultFont" above is sufficient as this will be inherited by all others
  public Font modifyFont(Object key, Font original) {
    return original;
  }

  // Scaling Radio or CheckBox button icons leads to really weird artifacts in Nimbus? Disable
  public Icon modifyIcon(Object key, Icon original) {
    return original;
  }
}
+1

All Articles