Support multi-monitor configurations in editor

This commit is contained in:
Dmitry Batrak
2016-12-27 12:30:07 +03:00
parent 7849cb87d2
commit dc0bf952e1
20 changed files with 327 additions and 196 deletions
@@ -330,4 +330,8 @@ public class UISettings extends SimpleModificationTracker implements PersistentS
public static void setupComponentAntialiasing(JComponent component) {
component.putClientProperty(SwingUtilities2.AA_TEXT_PROPERTY_KEY, AntialiasingType.getAAHintForSwingComponent());
}
public static void setupEditorAntialiasing(JComponent component) {
component.putClientProperty(SwingUtilities2.AA_TEXT_PROPERTY_KEY, getInstance().EDITOR_AA_TYPE.getTextInfo());
}
}
@@ -35,6 +35,7 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
@@ -116,10 +117,14 @@ public class ParameterHintsPresentationManager implements Disposable {
if (metrics != null) {
Font font = metrics.getFont();
if (!familyName.equals(font.getFamily()) || size != font.getSize()) metrics = null;
else {
FontRenderContext currentContext = FontInfo.getFontRenderContext(editor.getContentComponent());
if (currentContext.equals(metrics.metrics.getFontRenderContext())) metrics = null;
}
}
if (metrics == null) {
Font font = new Font(familyName, Font.PLAIN, size);
metrics = new MyFontMetrics(font);
metrics = new MyFontMetrics(editor, font);
editor.putUserData(HINT_FONT_METRICS, metrics);
}
return metrics;
@@ -129,11 +134,10 @@ public class ParameterHintsPresentationManager implements Disposable {
private final FontMetrics metrics;
private final int lineHeight;
private MyFontMetrics(Font font) {
metrics = FontInfo.createReferenceGraphics().getFontMetrics(font);
private MyFontMetrics(Editor editor, Font font) {
metrics = editor.getContentComponent().getFontMetrics(font);
// We assume this will be a better approximation to a real line height for a given font
lineHeight = (int)Math.ceil(metrics.getFont().createGlyphVector(metrics.getFontRenderContext(), "Ap")
.getVisualBounds().getHeight());
lineHeight = (int)Math.ceil(font.createGlyphVector(metrics.getFontRenderContext(), "Ap").getVisualBounds().getHeight());
}
private Font getFont() {
@@ -458,7 +458,7 @@ public class LookupCellRenderer implements ListCellRenderer {
Set<Font> fonts = ContainerUtil.newHashSet();
FontPreferences fontPreferences = myLookup.getFontPreferences();
for (int i = 0; i < sampleString.length(); i++) {
fonts.add(ComplementaryFontsRegistry.getFontAbleToDisplay(sampleString.charAt(i), Font.PLAIN, fontPreferences).getFont());
fonts.add(ComplementaryFontsRegistry.getFontAbleToDisplay(sampleString.charAt(i), Font.PLAIN, fontPreferences, null).getFont());
}
eachFont: for (Font font : fonts) {
@@ -857,7 +857,7 @@ public class TextWithMarkupProcessor extends CopyPastePostProcessor<RawTextWithM
for (; myCurrentOffset < myEndOffset; myCurrentOffset++) {
FontInfo fontInfo = ComplementaryFontsRegistry.getFontAbleToDisplay(myCharSequence.charAt(myCurrentOffset),
myFontStyle,
myFontPreferences);
myFontPreferences, null);
String fontFamilyName = fontInfo.getFont().getFamily();
if (myCurrentFontFamilyName == null) {
@@ -477,7 +477,8 @@ public final class EditorUtil {
@NotNull
public static FontInfo fontForChar(final char c, @JdkConstants.FontStyle int style, @NotNull Editor editor) {
EditorColorsScheme colorsScheme = editor.getColorsScheme();
return ComplementaryFontsRegistry.getFontAbleToDisplay(c, style, colorsScheme.getFontPreferences());
return ComplementaryFontsRegistry.getFontAbleToDisplay(c, style, colorsScheme.getFontPreferences(),
FontInfo.getFontRenderContext(editor.getContentComponent()));
}
public static Icon scaleIconAccordingEditorFont(Icon icon, Editor editor) {
@@ -16,9 +16,6 @@
package com.intellij.openapi.editor.impl;
import com.intellij.Patches;
import com.intellij.ide.ui.AntialiasingType;
import com.intellij.ide.ui.UISettings;
import com.intellij.ide.ui.UISettingsListener;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.colors.FontPreferences;
@@ -30,6 +27,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.util.*;
import java.util.List;
@@ -42,11 +40,11 @@ public class ComplementaryFontsRegistry {
private static final Object lock = new String("common lock");
private static final List<String> ourFontNames;
private static final Map<String, Pair<String, Integer>[]> ourStyledFontMap = new HashMap<>();
private static final LinkedHashMap<FontKey, FontInfo> ourUsedFonts;
private static FontKey ourSharedKeyInstance = new FontKey("", 0, Font.PLAIN, false);
private static FontInfo ourSharedDefaultFont;
private static final LinkedHashMap<FontFaceKey, FallBackInfo> ourUsedFonts;
public static final FallBackInfo DEFAULT_FONT_INFO = new FallBackInfo("", Font.PLAIN, Font.PLAIN);
private static FontFaceKey ourSharedKeyInstance = new FontFaceKey("", Font.PLAIN);
private static FallBackInfo ourSharedFallBackInfo = DEFAULT_FONT_INFO;
private static final TIntHashSet ourUndisplayableChars = new TIntHashSet();
private static boolean ourOldUseAntialiasing;
// This matches style detection in JDK (class sun.font.Font2D)
private static final String[] BOLD_NAMES = {"bold", "demibold", "demi-bold", "demi bold", "negreta", "demi" };
@@ -66,63 +64,9 @@ public class ComplementaryFontsRegistry {
FONT_NAME_TO_STYLE.put("Hasklig-BoldIt", Font.BOLD | Font.ITALIC);
}
static {
final UISettings settings = UISettings.getInstance();
ourOldUseAntialiasing = !AntialiasingType.OFF.equals(settings.EDITOR_AA_TYPE);
// Reset font info on 'use antialiasing' setting change.
// Assuming that the listener is notified from the EDT only.
ApplicationManager.getApplication().getMessageBus().connect().subscribe(UISettingsListener.TOPIC, new UISettingsListener() {
@Override
public void uiSettingsChanged(UISettings uiSettings) {
if (ourOldUseAntialiasing ^ !AntialiasingType.OFF.equals(settings.EDITOR_AA_TYPE)) {
ourOldUseAntialiasing = !AntialiasingType.OFF.equals(settings.EDITOR_AA_TYPE);
for (FontInfo fontInfo : ourUsedFonts.values()) {
fontInfo.reset();
}
ourUsedFonts.clear();
}
}
});
}
private ComplementaryFontsRegistry() {
}
private static class FontKey {
private String myFamilyName;
private int mySize;
private int myStyle;
private boolean myUseLigatures;
public FontKey(@NotNull String familyName, final int size, @JdkConstants.FontStyle int style, boolean useLigatures) {
myFamilyName = familyName;
mySize = size;
myStyle = style;
myUseLigatures = useLigatures;
}
public boolean equals(final Object o) {
if (this == o) return true;
final FontKey fontKey = (FontKey)o;
if (mySize != fontKey.mySize) return false;
if (myStyle != fontKey.myStyle) return false;
if (myUseLigatures != fontKey.myUseLigatures) return false;
return myFamilyName.equals(fontKey.myFamilyName);
}
public int hashCode() {
int result = myFamilyName.hashCode();
result = 29 * result + mySize;
result = 29 * result + myStyle;
if (myUseLigatures) {
result = 29 * result + 1;
}
return result;
}
}
@NonNls private static final String BOLD_SUFFIX = ".bold";
@NonNls private static final String ITALIC_SUFFIX = ".italic";
@@ -185,8 +129,21 @@ public class ComplementaryFontsRegistry {
return Font.PLAIN;
}
/**
* @deprecated Use {{@link #getFontAbleToDisplay(int, int, FontPreferences, FontRenderContext)}} instead.
*/
@NotNull
public static FontInfo getFontAbleToDisplay(int codePoint, @JdkConstants.FontStyle int style, @NotNull FontPreferences preferences) {
return getFontAbleToDisplay(codePoint, style, preferences, null);
}
/**
* If you intend to use font metrics from returned {@link FontInfo} object,
* pass not-null correct {@link FontRenderContext} to this method.
*/
@NotNull
public static FontInfo getFontAbleToDisplay(int codePoint, @JdkConstants.FontStyle int style, @NotNull FontPreferences preferences,
FontRenderContext context) {
boolean tryDefaultFont = true;
List<String> fontFamilies = preferences.getEffectiveFontFamilies();
boolean useLigatures = preferences.useLigatures();
@@ -194,7 +151,7 @@ public class ComplementaryFontsRegistry {
//noinspection ForLoopReplaceableByForEach
for (int i = 0, len = fontFamilies.size(); i < len; ++i) { // avoid foreach, it instantiates ArrayList$Itr, this traversal happens very often
final String fontFamily = fontFamilies.get(i);
result = doGetFontAbleToDisplay(codePoint, preferences.getSize(fontFamily), style, fontFamily, useLigatures);
result = doGetFontAbleToDisplay(codePoint, preferences.getSize(fontFamily), style, fontFamily, useLigatures, context);
if (result != null) {
return result;
}
@@ -205,30 +162,43 @@ public class ComplementaryFontsRegistry {
size = preferences.getSize(fontFamilies.get(0));
}
if (tryDefaultFont) {
result = doGetFontAbleToDisplay(codePoint, size, style, FontPreferences.DEFAULT_FONT_NAME, useLigatures);
result = doGetFontAbleToDisplay(codePoint, size, style, FontPreferences.DEFAULT_FONT_NAME, useLigatures, context);
if (result != null) {
return result;
}
}
result = doGetFontAbleToDisplay(codePoint, size, style, useLigatures);
result = doGetFontAbleToDisplay(codePoint, size, style, useLigatures, context);
if (LOG.isDebugEnabled()) {
LOG.debug("Fallback font: " + result.getFont().getFontName());
}
return result;
}
/**
* @deprecated Use {{@link #getFontAbleToDisplay(int, int, int, String, FontRenderContext)}}
*/
@NotNull
public static FontInfo getFontAbleToDisplay(int codePoint, int size, @JdkConstants.FontStyle int style, @NotNull String defaultFontFamily) {
FontInfo result = doGetFontAbleToDisplay(codePoint, size, style, defaultFontFamily, false);
return getFontAbleToDisplay(codePoint, size, style, defaultFontFamily, null);
}
/**
* If you intend to use font metrics from returned {@link FontInfo} object,
* pass not-null correct {@link FontRenderContext} to this method.
*/
@NotNull
public static FontInfo getFontAbleToDisplay(int codePoint, int size, @JdkConstants.FontStyle int style, @NotNull String defaultFontFamily,
FontRenderContext context) {
FontInfo result = doGetFontAbleToDisplay(codePoint, size, style, defaultFontFamily, false, context);
if (result != null) {
return result;
}
return doGetFontAbleToDisplay(codePoint, size, style, false);
return doGetFontAbleToDisplay(codePoint, size, style, false, context);
}
@Nullable
private static FontInfo doGetFontAbleToDisplay(int codePoint, int size, @JdkConstants.FontStyle int originalStyle,
@NotNull String defaultFontFamily, boolean useLigatures) {
@NotNull String defaultFontFamily, boolean useLigatures, FontRenderContext context) {
synchronized (lock) {
@JdkConstants.FontStyle int style = originalStyle;
if (Patches.JDK_MAC_FONT_STYLE_DETECTION_WORKAROUND && style > 0 && style < 4) {
@@ -238,69 +208,177 @@ public class ComplementaryFontsRegistry {
style = replacement[style].second;
}
}
if (ourSharedKeyInstance.mySize == size &&
ourSharedKeyInstance.myStyle == style &&
ourSharedKeyInstance.myFamilyName != null &&
FallBackInfo fallBackInfo;
if (ourSharedKeyInstance.myStyle == style &&
ourSharedKeyInstance.myFamilyName.equals(defaultFontFamily) &&
ourSharedKeyInstance.myUseLigatures == useLigatures &&
ourSharedDefaultFont != null &&
( codePoint < 128 ||
ourSharedDefaultFont.canDisplay(codePoint)
)
) {
return ourSharedDefaultFont;
}
ourSharedKeyInstance.myFamilyName = defaultFontFamily;
ourSharedKeyInstance.mySize = size;
ourSharedKeyInstance.myStyle = style;
ourSharedKeyInstance.myUseLigatures = useLigatures;
FontInfo defaultFont = ourUsedFonts.get(ourSharedKeyInstance);
if (defaultFont == null) {
defaultFont = new FontInfo(defaultFontFamily, size, style, originalStyle, useLigatures);
ourUsedFonts.put(ourSharedKeyInstance, defaultFont);
ourSharedKeyInstance = new FontKey("", 0, Font.PLAIN, false);
}
ourSharedDefaultFont = defaultFont;
if (codePoint < 128 || defaultFont.canDisplay(codePoint)) {
return defaultFont;
ourSharedFallBackInfo.canDisplay(codePoint)) {
fallBackInfo = ourSharedFallBackInfo;
}
else {
return null;
ourSharedKeyInstance.myFamilyName = defaultFontFamily;
ourSharedKeyInstance.myStyle = style;
fallBackInfo = ourUsedFonts.get(ourSharedKeyInstance);
if (fallBackInfo == null) {
fallBackInfo = new FallBackInfo(defaultFontFamily, style, originalStyle);
ourUsedFonts.put(ourSharedKeyInstance.clone(), fallBackInfo);
}
}
ourSharedFallBackInfo = fallBackInfo;
return fallBackInfo.canDisplay(codePoint) ? fallBackInfo.getFontInfo(size, useLigatures, context) : null;
}
}
@NotNull
private static FontInfo doGetFontAbleToDisplay(int codePoint, int size, @JdkConstants.FontStyle int style, boolean useLigatures) {
private static FontInfo doGetFontAbleToDisplay(int codePoint, int size, @JdkConstants.FontStyle int style, boolean useLigatures,
FontRenderContext context) {
synchronized (lock) {
if (ourUndisplayableChars.contains(codePoint)) return ourSharedDefaultFont;
final Collection<FontInfo> descriptors = ourUsedFonts.values();
for (FontInfo font : descriptors) {
if (font.getSize() == size &&
font.getStyle() == style &&
font.areLigaturesEnabled() == useLigatures &&
font.canDisplay(codePoint)) {
return font;
FallBackInfo fallBackInfo = DEFAULT_FONT_INFO;
if (!ourUndisplayableChars.contains(codePoint)) {
final Collection<FallBackInfo> descriptors = ourUsedFonts.values();
for (FallBackInfo info : descriptors) {
if (info.myOriginalStyle == style &&
info.canDisplay(codePoint)) {
fallBackInfo = info;
break;
}
}
if (fallBackInfo == DEFAULT_FONT_INFO) {
for (int i = 0; i < ourFontNames.size(); i++) {
String name = ourFontNames.get(i);
FallBackInfo info = new FallBackInfo(name, style, style);
if (info.canDisplay(codePoint)) {
ourUsedFonts.put(new FontFaceKey(name, style), info);
ourFontNames.remove(i);
fallBackInfo = info;
break;
}
}
if (fallBackInfo == DEFAULT_FONT_INFO) {
ourUndisplayableChars.add(codePoint);
}
}
}
return fallBackInfo.getFontInfo(size, useLigatures, context);
}
}
for (int i = 0; i < ourFontNames.size(); i++) {
String name = ourFontNames.get(i);
FontInfo font = new FontInfo(name, size, style, style, useLigatures);
if (font.canDisplay(codePoint)) {
ourUsedFonts.put(new FontKey(name, size, style, useLigatures), font);
ourFontNames.remove(i);
return font;
}
private static class FontFaceKey implements Cloneable {
private String myFamilyName;
private int myStyle;
private FontFaceKey(String familyName, int style) {
myFamilyName = familyName;
myStyle = style;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FontFaceKey key = (FontFaceKey)o;
if (myStyle != key.myStyle) return false;
if (!myFamilyName.equals(key.myFamilyName)) return false;
return true;
}
@Override
public int hashCode() {
int result = myFamilyName.hashCode();
result = 31 * result + myStyle;
return result;
}
@Override
protected FontFaceKey clone() {
try {
return (FontFaceKey)super.clone();
}
catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}
ourUndisplayableChars.add(codePoint);
private static class FontKey implements Cloneable {
private int mySize;
private boolean myUseLigatures;
private FontRenderContext myContext;
return ourSharedDefaultFont;
private FontKey(int size, boolean useLigatures, FontRenderContext context) {
mySize = size;
myUseLigatures = useLigatures;
myContext = context;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FontKey key = (FontKey)o;
if (mySize != key.mySize) return false;
if (myUseLigatures != key.myUseLigatures) return false;
if (myContext != null ? !myContext.equals(key.myContext) : key.myContext != null) return false;
return true;
}
@Override
public int hashCode() {
int result = mySize;
result = 31 * result + (myUseLigatures ? 1 : 0);
result = 31 * result + (myContext != null ? myContext.hashCode() : 0);
return result;
}
@Override
protected FontKey clone() {
try {
return (FontKey)super.clone();
}
catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}
private static class FallBackInfo {
private final Font myBaseFont;
private final int myOriginalStyle;
private final Map<FontKey, FontInfo> myFontInfoMap = new HashMap<>();
private final FontKey myLastFontKey = new FontKey(-1, false, new FontRenderContext(null, false, false));
private FontInfo myLastFontInfo;
private FallBackInfo(String familyName, @JdkConstants.FontStyle int style, int originalStyle) {
myBaseFont = new Font(familyName, style, 1);
myOriginalStyle = originalStyle;
}
private boolean canDisplay(int codePoint) {
return codePoint < 128 || myBaseFont.canDisplay(codePoint);
}
private FontInfo getFontInfo(int size, boolean useLigatures, FontRenderContext fontRenderContext) {
if (myLastFontKey.mySize == size &&
myLastFontKey.myUseLigatures == useLigatures &&
Objects.equals(myLastFontKey.myContext, fontRenderContext)) {
return myLastFontInfo;
}
myLastFontKey.mySize = size;
myLastFontKey.myUseLigatures = useLigatures;
myLastFontKey.myContext = fontRenderContext;
FontInfo fontInfo = myFontInfoMap.get(myLastFontKey);
if (fontInfo == null) {
fontInfo = new FontInfo(myBaseFont.getName(), size, myBaseFont.getStyle(), myOriginalStyle, useLigatures, fontRenderContext);
myFontInfoMap.put(myLastFontKey.clone(), fontInfo);
}
myLastFontInfo = fontInfo;
return fontInfo;
}
}
}
@@ -20,6 +20,7 @@ import gnu.trove.TObjectIntHashMap;
import org.intellij.lang.annotations.JdkConstants;
import java.awt.*;
import java.awt.font.FontRenderContext;
/**
* Not thread-safe. Performs caching of char widths, so cache reset must be invoked (via {@link #clearSymbolWidthCache()} method) when
@@ -46,6 +47,7 @@ public class DefaultEditorTextRepresentationHelper implements EditorTextRepresen
* {@link Editor#getColorsScheme()} often due to contention in 'assert read access'.
*/
private final Editor myEditor;
private FontRenderContext myFontRenderContext;
public DefaultEditorTextRepresentationHelper(Editor editor) {
myEditor = editor;
@@ -67,7 +69,8 @@ public class DefaultEditorTextRepresentationHelper implements EditorTextRepresen
return result;
}
Key key = mySharedKey.clone();
FontInfo font = ComplementaryFontsRegistry.getFontAbleToDisplay(c, key.fontType, myEditor.getColorsScheme().getFontPreferences());
FontInfo font = ComplementaryFontsRegistry.getFontAbleToDisplay(c, key.fontType, myEditor.getColorsScheme().getFontPreferences(),
myFontRenderContext);
result = font.charWidth(c);
if (mySymbolWidthCache.size() >= MAX_SYMBOLS_WIDTHS_CACHE_SIZE) {
// Don't expect to be here.
@@ -81,6 +84,12 @@ public class DefaultEditorTextRepresentationHelper implements EditorTextRepresen
mySymbolWidthCache.clear();
}
public void updateContext() {
FontRenderContext oldContext = myFontRenderContext;
myFontRenderContext = FontInfo.getFontRenderContext(myEditor.getContentComponent());
if (!myFontRenderContext.equals(oldContext)) clearSymbolWidthCache();
}
private static class Key {
@JdkConstants.FontStyle private int fontType;
private int c;
@@ -363,6 +363,7 @@ public class EditorComponentImpl extends JTextComponent implements Scrollable, D
// work. We do however need to provide a TextUI implementation since some
// screen reader support code will invoke it
setUI(new EditorAccessibilityTextUI());
UISettings.setupEditorAntialiasing(this);
invalidate();
}
@@ -147,8 +147,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
private static final Cursor EMPTY_CURSOR;
static {
ComplementaryFontsRegistry.getFontAbleToDisplay(' ', 0, Font.PLAIN, UIManager.getFont("Label.font").getFamily()); // load costly font info
Cursor emptyCursor = null;
if (!GraphicsEnvironment.isHeadless()) {
try {
@@ -17,20 +17,18 @@ package com.intellij.openapi.editor.impl;
import com.intellij.Patches;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.ex.util.EditorUIUtil;
import com.intellij.openapi.editor.impl.view.FontLayoutService;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import gnu.trove.TIntHashSet;
import org.intellij.lang.annotations.JdkConstants;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import sun.font.FontDesignMetrics;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FilenameFilter;
import java.util.*;
@@ -43,30 +41,47 @@ public class FontInfo {
private static final Logger LOG = Logger.getInstance(FontInfo.class);
private static final boolean USE_ALTERNATIVE_CAN_DISPLAY_PROCEDURE = Registry.is("ide.mac.fix.font.fallback");
private static final FontRenderContext DUMMY_CONTEXT = new FontRenderContext(null, false, false);
private static final FontRenderContext DEFAULT_CONTEXT = new FontRenderContext(null, false, false);
private static final Font DUMMY_FONT = new Font(null);
private final Font myFont;
private final int mySize;
@JdkConstants.FontStyle private final int myStyle;
private final boolean myUseLigatures;
private final TIntHashSet mySafeCharacters = new TIntHashSet();
private final FontRenderContext myContext;
private FontMetrics myFontMetrics = null;
/**
* @deprecated Use {@link #FontInfo(String, int, int, boolean, FontRenderContext)} instead.
*/
public FontInfo(final String familyName, final int size, @JdkConstants.FontStyle int style) {
this(familyName, size, style, false);
this(familyName, size, style, style, false, null);
}
/**
* @deprecated Use {@link #FontInfo(String, int, int, boolean, FontRenderContext)} instead.
*/
public FontInfo(final String familyName, final int size, @JdkConstants.FontStyle int style, boolean useLigatures) {
this(familyName, size, style, style, useLigatures);
this(familyName, size, style, useLigatures, null);
}
FontInfo(final String familyName, final int size,
@JdkConstants.FontStyle int style, @JdkConstants.FontStyle int realStyle, boolean useLigatures) {
/**
* To get valid font metrics from this {@link FontInfo} instance, pass valid {@link FontRenderContext} here as a parameter.
*/
public FontInfo(final String familyName, final int size, @JdkConstants.FontStyle int style, boolean useLigatures,
FontRenderContext fontRenderContext) {
this(familyName, size, style, style, useLigatures, fontRenderContext);
}
FontInfo(final String familyName, final int size,
@JdkConstants.FontStyle int style, @JdkConstants.FontStyle int realStyle, boolean useLigatures, FontRenderContext context) {
mySize = size;
myStyle = style;
myUseLigatures = useLigatures;
Font font = new Font(familyName, style, size);
myFont = useLigatures ? getFontWithLigaturesEnabled(font, realStyle) : font;
myContext = context;
}
@NotNull
@@ -155,7 +170,7 @@ public class FontInfo {
private boolean canDisplayImpl(int codePoint) {
if (!Character.isValidCodePoint(codePoint)) return false;
if (USE_ALTERNATIVE_CAN_DISPLAY_PROCEDURE) {
return myFont.createGlyphVector(DUMMY_CONTEXT, new String(new int[]{codePoint}, 0, 1)).getGlyphCode(0) > 0;
return myFont.createGlyphVector(DEFAULT_CONTEXT, new String(new int[]{codePoint}, 0, 1)).getGlyphCode(0) > 0;
}
else {
return myFont.canDisplay(codePoint);
@@ -176,27 +191,20 @@ public class FontInfo {
return FontLayoutService.getInstance().charWidth2D(metrics, codePoint);
}
public FontMetrics fontMetrics() {
public synchronized FontMetrics fontMetrics() {
if (myFontMetrics == null) {
// We need to use antialising-aware font metrics because we've alrady encountered a situation when non-antialiased symbol
// width is not equal to the antialiased one (IDEA-81539).
final Graphics graphics = createReferenceGraphics();
graphics.setFont(myFont);
myFontMetrics = graphics.getFontMetrics();
myFontMetrics = FontDesignMetrics.getMetrics(myFont, myContext == null ? getFontRenderContext(null) : myContext);
}
return myFontMetrics;
}
public static Graphics2D createReferenceGraphics() {
Graphics2D graphics = (Graphics2D)UIUtil.createImage(1, 1, BufferedImage.TYPE_INT_RGB).getGraphics();
EditorUIUtil.setupAntialiasing(graphics);
return graphics;
public static FontRenderContext getFontRenderContext(Component component) {
if (component == null) {
return DEFAULT_CONTEXT;
}
return component.getFontMetrics(DUMMY_FONT).getFontRenderContext();
}
void reset() {
myFontMetrics = null;
}
public int getSize() {
return mySize;
}
@@ -209,7 +217,11 @@ public class FontInfo {
public boolean areLigaturesEnabled() {
return myUseLigatures;
}
public FontRenderContext getFontRenderContext() {
return myContext;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -24,10 +24,7 @@ import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.ex.DocumentEx;
import com.intellij.openapi.editor.ex.ScrollingModelEx;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.editor.impl.DocumentImpl;
import com.intellij.openapi.editor.impl.EditorImpl;
import com.intellij.openapi.editor.impl.SoftWrapModelImpl;
import com.intellij.openapi.editor.impl.TextChangeImpl;
import com.intellij.openapi.editor.impl.*;
import com.intellij.openapi.editor.impl.softwrap.SoftWrapDrawingType;
import com.intellij.openapi.editor.impl.softwrap.SoftWrapImpl;
import com.intellij.openapi.editor.impl.softwrap.SoftWrapPainter;
@@ -245,6 +242,10 @@ public class SoftWrapApplianceManager implements Dumpable {
myContext.reset();
myOffset2fontType.clear();
myOffset2widthInPixels.clear();
EditorTextRepresentationHelper editorTextRepresentationHelper = SoftWrapModelImpl.getEditorTextRepresentationHelper(myEditor);
if (editorTextRepresentationHelper instanceof DefaultEditorTextRepresentationHelper) {
((DefaultEditorTextRepresentationHelper)editorTextRepresentationHelper).updateContext();
}
// Define start of the visual line that holds target range start.
final int start = event.getStartOffset();
@@ -16,12 +16,12 @@
package com.intellij.openapi.editor.impl.view;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.impl.FontInfo;
import com.intellij.util.BitUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.Rectangle2D;
import java.util.Arrays;
@@ -32,21 +32,21 @@ import java.util.Arrays;
class ComplexTextFragment extends TextFragment {
private static final Logger LOG = Logger.getInstance(ComplexTextFragment.class);
private static final double CLIP_MARGIN = 1e4;
@NotNull
private final GlyphVector myGlyphVector;
@Nullable
private final short[] myCodePoint2Offset; // Start offset of each Unicode code point in the fragment
// (null if each code point takes one char).
// We expect no more than 1025 chars in a fragment, so 'short' should be enough.
ComplexTextFragment(@NotNull char[] lineChars, int start, int end, boolean isRtl,
@NotNull Font font, @NotNull FontRenderContext fontRenderContext) {
ComplexTextFragment(@NotNull char[] lineChars, int start, int end, boolean isRtl, @NotNull FontInfo fontInfo) {
super(end - start);
assert start >= 0;
assert end <= lineChars.length;
assert start < end;
myGlyphVector = FontLayoutService.getInstance().layoutGlyphVector(font, fontRenderContext, lineChars, start, end, isRtl);
myGlyphVector = FontLayoutService.getInstance().layoutGlyphVector(fontInfo.getFont(), fontInfo.getFontRenderContext(),
lineChars, start, end, isRtl);
int numChars = end - start;
int numGlyphs = myGlyphVector.getNumGlyphs();
float totalWidth = (float)myGlyphVector.getGlyphPosition(numGlyphs).getX();
@@ -106,7 +106,7 @@ class ComplexTextFragment extends TextFragment {
}
}
}
private void setCharPosition(int logicalCharIndex, float x, boolean isRtl, int numChars) {
int charPosition = isRtl ? numChars - logicalCharIndex - 2 : logicalCharIndex;
if (charPosition >= 0 && charPosition < numChars - 1) {
@@ -22,6 +22,8 @@ import com.intellij.openapi.editor.FoldRegion;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.editor.VisualPosition;
import com.intellij.openapi.editor.colors.EditorFontType;
import com.intellij.openapi.editor.event.VisibleAreaEvent;
import com.intellij.openapi.editor.event.VisibleAreaListener;
import com.intellij.openapi.editor.ex.DocumentEx;
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
import com.intellij.openapi.editor.ex.util.EditorUtil;
@@ -36,7 +38,10 @@ import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;
import javax.swing.*;
import java.awt.*;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.font.FontRenderContext;
import java.text.Bidi;
@@ -46,7 +51,7 @@ import java.text.Bidi;
*
* Also contains a cache of several font-related quantities (line height, space width, etc).
*/
public class EditorView implements TextDrawingCallback, Disposable, Dumpable {
public class EditorView implements TextDrawingCallback, Disposable, Dumpable, HierarchyListener, VisibleAreaListener {
private static Key<LineLayout> FOLD_REGION_TEXT_LAYOUT = Key.create("text.layout");
private final EditorImpl myEditor;
@@ -76,7 +81,6 @@ public class EditorView implements TextDrawingCallback, Disposable, Dumpable {
private final Object myLock = new Object();
public EditorView(EditorImpl editor) {
setFontRenderContext();
myEditor = editor;
myDocument = editor.getDocument();
@@ -86,7 +90,10 @@ public class EditorView implements TextDrawingCallback, Disposable, Dumpable {
myTextLayoutCache = new TextLayoutCache(this);
myLogicalPositionCache = new LogicalPositionCache(this);
myTabFragment = new TabFragment(this);
myEditor.getContentComponent().addHierarchyListener(this);
myEditor.getScrollingModel().addVisibleAreaListener(this);
Disposer.register(this, myLogicalPositionCache);
Disposer.register(this, myTextLayoutCache);
Disposer.register(this, mySizeManager);
@@ -122,8 +129,21 @@ public class EditorView implements TextDrawingCallback, Disposable, Dumpable {
@Override
public void dispose() {
myEditor.getScrollingModel().removeVisibleAreaListener(this);
myEditor.getContentComponent().removeHierarchyListener(this);
}
@Override
public void hierarchyChanged(HierarchyEvent e) {
if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 && e.getComponent().isShowing()) {
checkFontRenderContext();
}
}
@Override
public void visibleAreaChanged(VisibleAreaEvent e) {
checkFontRenderContext();
}
public int yToVisualLine(int y) {
return myMapper.yToVisualLine(y);
}
@@ -481,12 +501,15 @@ public class EditorView implements TextDrawingCallback, Disposable, Dumpable {
}
private void setFontRenderContext() {
Graphics2D g = FontInfo.createReferenceGraphics();
try {
myFontRenderContext = g.getFontRenderContext();
}
finally {
g.dispose();
JComponent component = myEditor.getContentComponent();
myFontRenderContext = FontInfo.getFontRenderContext(component);
}
private void checkFontRenderContext() {
FontRenderContext oldContext = myFontRenderContext;
setFontRenderContext();
if (!myFontRenderContext.equals(oldContext)) {
myTextLayoutCache.resetToDocumentSize(false);
}
}
@@ -514,7 +537,7 @@ public class EditorView implements TextDrawingCallback, Disposable, Dumpable {
int getBidiFlags() {
return myBidiFlags;
}
private static void assertIsDispatchThread() {
ApplicationManager.getApplication().assertIsDispatchThread();
}
@@ -266,7 +266,7 @@ abstract class LineLayout {
char c = text[i];
if (c == '\t' && tabFragment != null) {
assert run.level == 0;
addTextFragmentIfNeeded(chunk, text, currentIndex, i, currentFontInfo, fontRenderContext, false);
addTextFragmentIfNeeded(chunk, text, currentIndex, i, currentFontInfo, false);
chunk.fragments.add(tabFragment);
currentFontInfo = null;
currentIndex = i + 1;
@@ -281,24 +281,23 @@ abstract class LineLayout {
surrogatePair = true;
}
}
FontInfo fontInfo = ComplementaryFontsRegistry.getFontAbleToDisplay(codePoint, fontStyle, fontPreferences);
FontInfo fontInfo = ComplementaryFontsRegistry.getFontAbleToDisplay(codePoint, fontStyle, fontPreferences, fontRenderContext);
if (!fontInfo.equals(currentFontInfo)) {
addTextFragmentIfNeeded(chunk, text, currentIndex, i, currentFontInfo, fontRenderContext, run.isRtl());
addTextFragmentIfNeeded(chunk, text, currentIndex, i, currentFontInfo, run.isRtl());
currentFontInfo = fontInfo;
currentIndex = i;
}
if (surrogatePair) i++;
}
}
addTextFragmentIfNeeded(chunk, text, currentIndex, end, currentFontInfo, fontRenderContext, run.isRtl());
addTextFragmentIfNeeded(chunk, text, currentIndex, end, currentFontInfo, run.isRtl());
assert !chunk.fragments.isEmpty();
}
private static void addTextFragmentIfNeeded(Chunk chunk, char[] chars, int from, int to, FontInfo fontInfo,
FontRenderContext fontRenderContext, boolean isRtl) {
private static void addTextFragmentIfNeeded(Chunk chunk, char[] chars, int from, int to, FontInfo fontInfo, boolean isRtl) {
if (to > from) {
assert fontInfo != null;
chunk.fragments.add(TextFragmentFactory.createTextFragment(chars, from, to, isRtl, fontInfo, fontRenderContext));
chunk.fragments.add(TextFragmentFactory.createTextFragment(chars, from, to, isRtl, fontInfo));
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,13 +19,10 @@ import com.intellij.openapi.editor.impl.FontInfo;
import org.jetbrains.annotations.NotNull;
import sun.swing.SwingUtilities2;
import java.awt.font.FontRenderContext;
class TextFragmentFactory {
public static TextFragment createTextFragment(@NotNull char[] lineChars, int start, int end, boolean isRtl,
@NotNull FontInfo fontInfo, @NotNull FontRenderContext fontRenderContext) {
public static TextFragment createTextFragment(@NotNull char[] lineChars, int start, int end, boolean isRtl, @NotNull FontInfo fontInfo) {
if (isRtl || fontInfo.getFont().hasLayoutAttributes() || isComplexText(lineChars, start, end)) {
return new ComplexTextFragment(lineChars, start, end, isRtl, fontInfo.getFont(), fontRenderContext);
return new ComplexTextFragment(lineChars, start, end, isRtl, fontInfo);
}
else {
return new SimpleTextFragment(lineChars, start, end, fontInfo);
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,6 +27,6 @@ import java.awt.*;
public class SuitableFontProviderImpl implements SuitableFontProvider {
@Override
public Font getFontAbleToDisplay(char c, int size, @JdkConstants.FontStyle int style, @NotNull String defaultFontFamily) {
return ComplementaryFontsRegistry.getFontAbleToDisplay(c, size, style, defaultFontFamily).getFont();
return ComplementaryFontsRegistry.getFontAbleToDisplay(c, size, style, defaultFontFamily, null).getFont();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
*/
package com.intellij.openapi.editor.impl.view;
import com.intellij.openapi.editor.impl.FontInfo;
import com.intellij.testFramework.AbstractMockGlyphVector;
import com.intellij.testFramework.MockFontLayoutService;
import com.intellij.util.ArrayUtil;
@@ -100,8 +101,9 @@ public class ComplexTextFragmentTest {
try {
int length = gv.getNumChars();
char[] text = new char[length];
FontInfo fontInfo = new FontInfo(Font.MONOSPACED, 1, Font.PLAIN, false, null);
ComplexTextFragment fragment = new ComplexTextFragment(text, 0, length, (gv.getLayoutFlags() & GlyphVector.FLAG_RUN_RTL) != 0,
new Font(null), new FontRenderContext(null, false, false));
fontInfo);
int[] charPositions = new int[length];
for (int i = 0; i < length; i++) {
charPositions[i] = (int)fragment.visualColumnToX(0, i + 1);
@@ -24,7 +24,7 @@ import java.awt.*;
public class FontUtil {
@NotNull
public static Font getFontAbleToDisplay(char c, int size, int style, @NotNull String family) {
return ComplementaryFontsRegistry.getFontAbleToDisplay(c, size, style, family).getFont();
return ComplementaryFontsRegistry.getFontAbleToDisplay(c, size, style, family, null).getFont();
}
@NotNull
@@ -88,7 +88,8 @@ public class XDebuggerInlayUtil {
FontPreferences fontPreferences = colorsScheme.getFontPreferences();
TextAttributes attributes = editor.getColorsScheme().getAttributes(DebuggerColors.INLINED_VALUES_EXECUTION_LINE);
int fontStyle = attributes == null ? Font.PLAIN : attributes.getFontType();
return ComplementaryFontsRegistry.getFontAbleToDisplay('a', fontStyle, fontPreferences);
return ComplementaryFontsRegistry.getFontAbleToDisplay('a', fontStyle, fontPreferences,
FontInfo.getFontRenderContext(editor.getContentComponent()));
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -293,7 +293,8 @@ public class JBTerminalPanel extends TerminalPanel implements FocusListener, Ter
}
public FontInfo fontForChar(final char c, @JdkConstants.FontStyle int style) {
return ComplementaryFontsRegistry.getFontAbleToDisplay(c, style, mySettingsProvider.getColorScheme().getConsoleFontPreferences());
return ComplementaryFontsRegistry.getFontAbleToDisplay(c, style, mySettingsProvider.getColorScheme().getConsoleFontPreferences(),
null);
}
@Override