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() {