From 95069ee9fde94cce34dd4c4b3ce36b148145e53c Mon Sep 17 00:00:00 2001 From: Anton Tarasov Date: Wed, 23 Mar 2016 17:59:14 +0300 Subject: [PATCH] IDEA-149337 UI fonts are too big The logic of detecting the default system font size on Linux is improved. The default font size is used to derive the IDEA UI scale factor. Another side of improvement goes into our custom Linux JDK. GTK L&F will be able to detect "Xft.dpi" value in any Linux Desktop Environment, not necessarily built on GTK (e.g. KDE). "Xft.dpi" value is an X server resource which defines the fonts DPI. The value is set by a Linux DE automatically, or in response to users's settings. With the new custom JDK, IDEA will get properly scaled font on virtually all Linux's we support. So, the logic of deriving the UI scale will be unified with what IDEA does on Windows. With Oracle JDK, IDEA will behave the same way on GTK Linux DE's, but on other Linux's it will fallback to the old approach of detecting the UI scale based on the X server DPI (if set). --- .../src/com/intellij/ide/ui/UISettings.java | 22 ++++-- .../actions/TogglePresentationModeAction.java | 3 +- .../intellij/ide/ui/laf/LafManagerImpl.java | 4 +- .../ide/ui/laf/darcula/DarculaLaf.java | 2 +- .../util/resources/misc/registry.properties | 3 + .../util/src/com/intellij/util/ui/JBUI.java | 77 +++++++------------ .../util/src/com/intellij/util/ui/UIUtil.java | 59 ++++++++++++-- 7 files changed, 103 insertions(+), 67 deletions(-) diff --git a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java index b6ccede3ea81..ac657aefb963 100644 --- a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java +++ b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java @@ -53,8 +53,15 @@ public class UISettings extends SimpleModificationTracker implements PersistentS /** Not tabbed pane. */ public static final int TABS_NONE = 0; + private String lafID; + public static UISettings getInstance() { - return ServiceManager.getService(UISettings.class); + UISettings instance = ServiceManager.getService(UISettings.class); + if (!instance.lafID.equals(UIManager.getLookAndFeel().getID())) { + // Re-init if LaF changed. + instance.init(); + } + return instance; } /** @@ -135,6 +142,10 @@ public class UISettings extends SimpleModificationTracker implements PersistentS private final EventDispatcher myDispatcher = EventDispatcher.create(UISettingsListener.class); public UISettings() { + init(); + } + + private void init() { tweakPlatformDefaults(); setSystemFontFaceAndSize(); @@ -142,6 +153,7 @@ public class UISettings extends SimpleModificationTracker implements PersistentS if (scrollToSource != null) { DEFAULT_AUTOSCROLL_TO_SOURCE = scrollToSource; } + lafID = UIManager.getLookAndFeel().getID(); } private void tweakPlatformDefaults() { @@ -181,11 +193,9 @@ public class UISettings extends SimpleModificationTracker implements PersistentS } private void setSystemFontFaceAndSize() { - if (FONT_FACE == null || FONT_SIZE <= 0) { - final Pair fontData = getSystemFontFaceAndSize(); - FONT_FACE = fontData.first; - FONT_SIZE = fontData.second; - } + final Pair fontData = getSystemFontFaceAndSize(); + FONT_FACE = fontData.first; + FONT_SIZE = fontData.second; } private static Pair getSystemFontFaceAndSize() { diff --git a/platform/platform-impl/src/com/intellij/ide/actions/TogglePresentationModeAction.java b/platform/platform-impl/src/com/intellij/ide/actions/TogglePresentationModeAction.java index 9d4134880911..628221c3950e 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/TogglePresentationModeAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/TogglePresentationModeAction.java @@ -35,6 +35,7 @@ import com.intellij.openapi.wm.impl.DesktopLayout; import com.intellij.openapi.wm.impl.IdeFrameImpl; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.JBUI; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -147,7 +148,7 @@ public class TogglePresentationModeAction extends AnAction implements DumbAware } } } - float scaleFactor = settings.PRESENTATION_MODE_FONT_SIZE / 12f; + float scaleFactor = settings.PRESENTATION_MODE_FONT_SIZE / UIUtil.DEF_SYSTEM_FONT_SIZE; ourSavedScaleFactor = JBUI.scale(1f); JBUI.setScaleFactor(scaleFactor); for (Object key : ourSavedValues.keySet()) { diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/LafManagerImpl.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/LafManagerImpl.java index 63afba65f4bb..168f79f93a9f 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/LafManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/LafManagerImpl.java @@ -729,7 +729,7 @@ public final class LafManagerImpl extends LafManager implements ApplicationCompo UISettings uiSettings = UISettings.getInstance(); if (uiSettings.OVERRIDE_NONIDEA_LAF_FONTS) { storeOriginalFontDefaults(uiDefaults); - JBUI.setScaleFactor(uiSettings.FONT_SIZE/12f); + JBUI.setScaleFactor(uiSettings.FONT_SIZE/UIUtil.DEF_SYSTEM_FONT_SIZE); initFontDefaults(uiDefaults, uiSettings.FONT_SIZE, new FontUIResource(uiSettings.FONT_FACE, Font.PLAIN, uiSettings.FONT_SIZE)); } else { @@ -745,7 +745,7 @@ public final class LafManagerImpl extends LafManager implements ApplicationCompo defaults.put(resource, lfDefaults.get(resource)); } } - JBUI.setScaleFactor(JBUI.Fonts.label().getSize()/12f); + JBUI.setScaleFactor(JBUI.Fonts.label().getSize()/UIUtil.DEF_SYSTEM_FONT_SIZE); } private void storeOriginalFontDefaults(UIDefaults defaults) { diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLaf.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLaf.java index e398cbfb0f12..0ae2a1dc3caa 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLaf.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLaf.java @@ -149,7 +149,7 @@ public class DarculaLaf extends BasicLookAndFeel { private static void applySystemFonts(UIDefaults defaults) { try { - String fqn = UIManager.getSystemLookAndFeelClassName(); + String fqn = UIUtil.getSystemLookAndFeelClassName(); Object systemLookAndFeel = Class.forName(fqn).newInstance(); final Method superMethod = BasicLookAndFeel.class.getDeclaredMethod("getDefaults"); superMethod.setAccessible(true); diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index aa87893bdef2..f157470a699f 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -468,6 +468,9 @@ cvs.roots.refresh.uses.vfs=true cvs.roots.refresh.uses.vfs.description=Should CVS roots refresh after update use VFS vcs.add.remove.silent=true +ide.ui.scale.override=false +ide.ui.scale=1.0 + ide.new.notification.enabled=false ide.new.notification.visible.count=2 diff --git a/platform/util/src/com/intellij/util/ui/JBUI.java b/platform/util/src/com/intellij/util/ui/JBUI.java index c12f42bfdb10..6139cfdca0f2 100644 --- a/platform/util/src/com/intellij/util/ui/JBUI.java +++ b/platform/util/src/com/intellij/util/ui/JBUI.java @@ -36,61 +36,35 @@ import java.awt.*; public class JBUI { private static final Logger LOG = Logger.getInstance("#com.intellij.util.ui.JBUI"); - private static float SCALE_FACTOR = calculateScaleFactor(); + private static float scaleFactor = 1.0f; - private static float calculateScaleFactor() { + static { + calculateScaleFactor(); + } + + private static void calculateScaleFactor() { if (SystemInfo.isMac) { LOG.info("UI scale factor: 1.0"); - return 1.0f; + scaleFactor = 1.0f; + return; } if (SystemProperties.has("hidpi") && !SystemProperties.is("hidpi")) { LOG.info("UI scale factor: 1.0"); - return 1.0f; + scaleFactor = 1.0f; + return; } - float s = 1f; + UIUtil.initSystemFontData(); + Pair fdata = UIUtil.getSystemFontData(); - // On Linux: rely on DPI - if (SystemInfo.isLinux) { - final int dpi = getSystemDPI(); - if (dpi < 120) s = 1f; - else if (dpi < 144) s = 1.25f; - else if (dpi < 168) s = 1.5f; - else if (dpi < 192) s = 1.75f; - else s = 2f; - - LOG.info("UI scale factor: " + s); - return s; - } - - int size = -1; - - // On Windows: rely on default system font - if (SystemInfo.isWindows) { - UIUtil.initSystemFontData(); - Pair fdata = UIUtil.getSystemFontData(); - if (fdata != null) size = fdata.getSecond(); - } - if (size == -1) { + int size; + if (fdata != null) { + size = fdata.getSecond(); + } else { size = Fonts.label().getSize(); } - if (size <= 13) s = 1.0f; - else if (size <= 16) s = 1.25f; - else if (size <= 18) s = 1.5f; - else if (size < 24) s = 1.75f; - else s = 2.0f; - - LOG.info("UI scale factor: " + s); - return s; - } - - private static int getSystemDPI() { - try { - return Toolkit.getDefaultToolkit().getScreenResolution(); - } catch (HeadlessException e) { - return 96; - } + setScaleFactor(size/UIUtil.DEF_SYSTEM_FONT_SIZE); } public static void setScaleFactor(float scale) { @@ -108,19 +82,22 @@ public class JBUI { //Default UI font size for Unity and Gnome is 15. Scaling factor 1.25f works badly on Linux scale = 1f; } - LOG.info("UI scale factor changed: " + scale); + if (scaleFactor == scale) { + return; + } + LOG.info("UI scale factor: " + scale); - SCALE_FACTOR = scale; + scaleFactor = scale; IconLoader.setScale(scale); } public static int scale(int i) { - return Math.round(SCALE_FACTOR * i); + return Math.round(scaleFactor * i); } public static int scaleFontSize(int fontSize) { - if (SCALE_FACTOR == 1.25f) return (int)(fontSize * 1.34f); - if (SCALE_FACTOR == 1.75f) return (int)(fontSize * 1.67f); + if (scaleFactor == 1.25f) return (int)(fontSize * 1.34f); + if (scaleFactor == 1.75f) return (int)(fontSize * 1.67f); return scale(fontSize); } @@ -177,7 +154,7 @@ public class JBUI { } public static float scale(float f) { - return f * SCALE_FACTOR; + return f * scaleFactor; } public static JBInsets insets(Insets insets) { @@ -185,7 +162,7 @@ public class JBUI { } public static boolean isHiDPI() { - return SCALE_FACTOR > 1.0f; + return scaleFactor > 1.0f; } public static class Fonts { diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index ebf1f3c325b0..38571a174207 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -323,6 +323,8 @@ public class UIUtil { private static volatile Pair ourSystemFontData = null; + public static final float DEF_SYSTEM_FONT_SIZE = 12f; // TODO: consider 12 * 1.33 to compensate JDK's 72dpi font scale + @NonNls private static final String ROOT_PANE = "JRootPane.future"; private static final Ref ourRetina = Ref.create(SystemInfo.isMac ? null : false); @@ -2542,9 +2544,16 @@ public class UIUtil { } } + public static String getSystemLookAndFeelClassName() { + // Force GTK LaF on Linux to let it retrieve system font settings + // with proper font scale based on Xft.dpi + return SystemInfo.isLinux ? "com.sun.java.swing.plaf.gtk.GTKLookAndFeel" : + UIManager.getSystemLookAndFeelClassName(); + } + public static void initDefaultLAF() { try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + UIManager.setLookAndFeel(getSystemLookAndFeelClassName()); initSystemFontData(); } catch (Exception ignore) {} @@ -2553,16 +2562,36 @@ public class UIUtil { public static void initSystemFontData() { if (ourSystemFontData != null) return; + // With JB Linux JDK the label font comes properly scaled based on Xft.dpi settings. Font font = getLabelFont(); - if (SystemInfo.isWindows) { + + Float forcedScale = null; + if (Registry.is("ide.ui.scale.override")) { + forcedScale = Float.valueOf((float)Registry.get("ide.ui.scale").asDouble()); + } + else if (SystemInfo.isLinux && !SystemInfo.isJetbrainsJvm) { + // With Oracle JDK: derive scale from X server DPI + float scale = getScreenScale(); + if (scale > 1f) { + forcedScale = Float.valueOf(scale); + } + // Or otherwise leave the detected font. It's undetermined if it's scaled or not. + // If it is (likely with GTK DE), then the UI scale will be derived from it, + // if it's not, then IDEA will start unscaled. This lets the users of GTK DEs + // not to bother about X server DPI settings. Users of other DEs (like KDE) + // will have to set X server DPI to meet their display. + } + else if (SystemInfo.isWindows) { //noinspection HardCodedStringLiteral Font winFont = (Font)Toolkit.getDefaultToolkit().getDesktopProperty("win.messagebox.font"); - if (winFont != null) font = winFont; + if (winFont != null) { + font = winFont; // comes scaled + } } - else if (SystemInfo.isLinux && JBUI.isHiDPI()) { - // We don't expect the default GUI font to be scaled on Linux and do it ourselves. - // TODO: this is valid until HIDPI support comes to J2D/Swing on Linux. - font = JBFont.create(font); + if (forcedScale != null) { + // With forced scale, we derive font from a hard-coded value as we cannot be sure + // the system font comes unscaled. + font = font.deriveFont(DEF_SYSTEM_FONT_SIZE * forcedScale.floatValue()); } ourSystemFontData = Pair.create(font.getName(), font.getSize()); } @@ -2572,6 +2601,22 @@ public class UIUtil { return ourSystemFontData; } + private static float getScreenScale() { + int dpi = 96; + try { + dpi = Toolkit.getDefaultToolkit().getScreenResolution(); + } catch (HeadlessException e) { + } + float scale = 1f; + if (dpi < 120) scale = 1f; + else if (dpi < 144) scale = 1.25f; + else if (dpi < 168) scale = 1.5f; + else if (dpi < 192) scale = 1.75f; + else scale = 2f; + + return scale; + } + public static void addKeyboardShortcut(final JComponent target, final AbstractButton button, final KeyStroke keyStroke) { target.registerKeyboardAction( new ActionListener() {