/** * Tries to find fonts that are capable to display all unicode symbols used by the current painter. */ @Override public void reinit() { // We use dummy component here in order to being able to work with font metrics. JLabel component = new JLabel(); myCanUse = true; for (Map.Entry<SoftWrapDrawingType, char[]> entry : mySymbols.entrySet()) { SoftWrapDrawingType type = entry.getKey(); char c = entry.getValue()[0]; FontInfo fontInfo = EditorUtil.fontForChar(c, Font.PLAIN, myEditor); if (!fontInfo.canDisplay(c)) { myCanUse = false; myFonts.put(type, null); myVGaps.put(type, null); myWidths[type.ordinal()] = 0; } else { myFonts.put(type, fontInfo); FontMetrics metrics = component.getFontMetrics(fontInfo.getFont()); myWidths[type.ordinal()] = metrics.charWidth(c); int vGap = metrics.getDescent(); myVGaps.put(type, vGap); } } }
public void advance() { myCurrentFontFamilyName = myNextFontFamilyName; myCurrentStartOffset = myCurrentOffset; for (; myCurrentOffset < myEndOffset; myCurrentOffset++) { FontInfo fontInfo = ComplementaryFontsRegistry.getFontAbleToDisplay(myCharSequence.charAt(myCurrentOffset), myFontStyle, myFontPreferences); String fontFamilyName = fontInfo.getFont().getFamily(); if (myCurrentFontFamilyName == null) { myCurrentFontFamilyName = fontFamilyName; } else if (!myCurrentFontFamilyName.equals(fontFamilyName)) { myNextFontFamilyName = fontFamilyName; break; } } }
/** * Tries to find fonts that are capable to display all unicode symbols used by the current painter. * * @param symbols target symbols to use for drawing * @param editor editor to use during font lookup * @return <code>true</code> if target font that is capable to display all unicode symbols used by the current painter is found; * <code>false</code> otherwise */ private boolean init(Map<SoftWrapDrawingType, Character> symbols, Editor editor) { // We use dummy component here in order to being able to work with font metrics. JLabel component = new JLabel(); for (Map.Entry<SoftWrapDrawingType, Character> entry : symbols.entrySet()) { FontInfo fontInfo = EditorUtil.fontForChar(entry.getValue(), Font.PLAIN, editor); if (!fontInfo.canDisplay(entry.getValue())) { return false; } char[] buffer = new char[1]; buffer[0] = entry.getValue(); mySymbols.put(entry.getKey(), buffer); myFonts.put(entry.getKey(), fontInfo); FontMetrics metrics = component.getFontMetrics(fontInfo.getFont()); myWidths[entry.getKey().ordinal()] = metrics.charWidth(buffer[0]); int vGap = metrics.getDescent(); myVGaps.put(entry.getKey(), vGap); } return true; }
@Override public int paint(@NotNull Graphics g, @NotNull SoftWrapDrawingType drawingType, int x, int y, int lineHeight) { FontInfo fontInfo = myFonts.get(drawingType); if (fontInfo != null) { char[] buffer = mySymbols.get(drawingType); int vGap = myVGaps.get(drawingType); myDrawingCallback.drawChars(g, buffer, 0, buffer.length, x, y + lineHeight - vGap, myColorHolder.getColor(), fontInfo); } return getMinDrawingWidth(drawingType); }
@Override public int paint(@NotNull Graphics g, @NotNull SoftWrapDrawingType drawingType, int x, int y, int lineHeight) { char[] buffer = mySymbols.get(drawingType); FontInfo fontInfo = myFonts.get(drawingType); int vGap = myVGaps.get(drawingType); myDrawingCallback.drawChars(g, buffer, 0, buffer.length, x, y + lineHeight - vGap, myColorHolder.getColor(), fontInfo); return getMinDrawingWidth(drawingType); }
SimpleTextFragment(@Nonnull char[] lineChars, int start, int end, @Nonnull FontInfo fontInfo) { super(end - start); myText = Arrays.copyOfRange(lineChars, start, end); myFont = fontInfo.getFont(); float x = 0; for (int i = 0; i < myText.length; i++) { x += fontInfo.charWidth2D(myText[i]); myCharPositions[i] = x; } }
public static TextFragment createTextFragment(@Nonnull char[] lineChars, int start, int end, boolean isRtl, @Nonnull FontInfo fontInfo) { if (isRtl || fontInfo.getFont().hasLayoutAttributes() || isComplexText(lineChars, start, end)) { return new ComplexTextFragment(lineChars, start, end, isRtl, fontInfo); } else { return new SimpleTextFragment(lineChars, start, end, fontInfo); } }
@Override public int paint(@Nonnull Graphics g, @Nonnull SoftWrapDrawingType drawingType, int x, int y, int lineHeight) { FontInfo fontInfo = myFonts.get(drawingType); if (fontInfo != null) { char[] buffer = mySymbols.get(drawingType); int vGap = myVGaps.get(drawingType); myDrawingCallback.drawChars(g, buffer, 0, buffer.length, x, y + lineHeight - vGap, myColorHolder.getColor(), fontInfo); } return getMinDrawingWidth(drawingType); }
private static FontInfo getFontInfo(@Nonnull Editor editor) { EditorColorsScheme colorsScheme = editor.getColorsScheme(); 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, FontInfo.getFontRenderContext(editor.getContentComponent())); }
@Override public void paint(@Nonnull Editor editor, @Nonnull Graphics g, @Nonnull Rectangle r) { TextAttributes attributes = editor.getColorsScheme().getAttributes(DebuggerColors.INLINED_VALUES_EXECUTION_LINE); if (attributes == null) return; Color fgColor = attributes.getForegroundColor(); if (fgColor == null) return; g.setColor(fgColor); FontInfo fontInfo = getFontInfo(editor); g.setFont(fontInfo.getFont()); FontMetrics metrics = fontInfo.fontMetrics(); g.drawString(myText, r.x, r.y + metrics.getAscent()); }
@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()); }
@Override public void drawChars(@NotNull Graphics g, @NotNull char[] data, int start, int end, int x, int y, Color color, FontInfo fontInfo) { myPainter.drawChars(g, data, start, end, x, y, color, fontInfo); }
@Override protected Font getFontToDisplay(char c, TextStyle style) { FontInfo fontInfo = fontForChar(c, style.hasOption(TextStyle.Option.BOLD) ? Font.BOLD : Font.PLAIN); return fontInfo.getFont(); }
public FontInfo fontForChar(final char c, @JdkConstants.FontStyle int style) { return ComplementaryFontsRegistry.getFontAbleToDisplay(c, style, mySettingsProvider.getColorScheme().getConsoleFontPreferences()); }
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()); }
public FontInfo fontForChar(final char c, @JdkConstants.FontStyle int style) { return ComplementaryFontsRegistry.getFontAbleToDisplay(c, style, mySettingsProvider.getColorScheme() .getConsoleFontPreferences()); }
@Nonnull public static FontInfo fontForChar(final char c, @JdkConstants.FontStyle int style, @Nonnull Editor editor) { EditorColorsScheme colorsScheme = editor.getColorsScheme(); return ComplementaryFontsRegistry.getFontAbleToDisplay(c, style, colorsScheme.getFontPreferences(), FontInfo.getFontRenderContext(editor.getContentComponent())); }
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)); } }
ComplexTextFragment(@Nonnull char[] lineChars, int start, int end, boolean isRtl, @Nonnull FontInfo fontInfo) { super(end - start); assert start >= 0; assert end <= lineChars.length; assert start < end; 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(); myCharPositions[numChars - 1] = totalWidth; int lastCharIndex = -1; float lastX = isRtl ? totalWidth : 0; float prevX = lastX; // Here we determine coordinates for boundaries between characters. // They will be used to place caret, last boundary coordinate is also defining the width of text fragment. // // We expect these positions to be ordered, so that when caret moves through text characters in some direction, corresponding text // offsets change monotonously (within the same-directionality fragment). // // Special case that we must account for is a ligature, when several adjacent characters are represented as a single glyph. // In a glyph vector this glyph is associated with the first character, // other characters either don't have an associated glyph, or they are associated with empty glyphs. // (in RTL case real glyph will be associated with first logical character, i.e. last visual character) for (int i = 0; i < numGlyphs; i++) { int visualGlyphIndex = isRtl ? numGlyphs - 1 - i : i; int charIndex = myGlyphVector.getGlyphCharIndex(visualGlyphIndex); if (charIndex > lastCharIndex) { Rectangle2D bounds = myGlyphVector.getGlyphLogicalBounds(visualGlyphIndex).getBounds2D(); if (!bounds.isEmpty()) { if (charIndex > lastCharIndex + 1) { for (int j = Math.max(0, lastCharIndex); j < charIndex; j++) { setCharPosition(j, prevX + (lastX - prevX) * (j - lastCharIndex + 1) / (charIndex - lastCharIndex), isRtl, numChars); } } float newX = isRtl ? Math.min(lastX, (float)bounds.getMinX()) : Math.max(lastX, (float)bounds.getMaxX()); newX = Math.max(0, Math.min(totalWidth, newX)); setCharPosition(charIndex, newX, isRtl, numChars); prevX = lastX; lastX = newX; lastCharIndex = charIndex; } } } if (lastCharIndex < numChars - 1) { for (int j = Math.max(0, lastCharIndex); j < numChars - 1; j++) { setCharPosition(j, prevX + (lastX - prevX) * (j - lastCharIndex + 1) / (numChars - lastCharIndex), isRtl, numChars); } } int codePointCount = Character.codePointCount(lineChars, start, end - start); if (codePointCount == numChars) { myCodePoint2Offset = null; } else { myCodePoint2Offset = new short[codePointCount]; int offset = 0; for (int i = 0; i < codePointCount; i++) { myCodePoint2Offset[i] = (short)(offset++); if (offset < numChars && Character.isHighSurrogate(lineChars[start + offset - 1]) && Character.isLowSurrogate(lineChars[start + offset])) { offset++; } } } }
private void setFontRenderContext(FontRenderContext context) { myFontRenderContext = context == null ? FontInfo.getFontRenderContext(myEditor.getContentComponent()) : context; }
@Override public void drawChars(@Nonnull Graphics g, @Nonnull char[] data, int start, int end, int x, int y, Color color, FontInfo fontInfo) { myPainter.drawChars(g, data, start, end, x, y, color, fontInfo); }
@Override public int calcWidthInPixels(@Nonnull Editor editor) { FontInfo fontInfo = getFontInfo(editor); return fontInfo.fontMetrics().stringWidth(myText); }
/** * Allows to answer what width in pixels is required to draw fragment of the given char array from <code>[start; end)</code> interval * at the given editor. * <p/> * Tabulation symbols is processed specially, i.e. it's ta * <p/> * <b>Note:</b> it's assumed that target text fragment remains to the single line, i.e. line feed symbols within it are not * treated specially. * * @param editor editor that will be used for target text representation * @param text target text holder * @param start offset within the given char array that points to target text start (inclusive) * @param end offset within the given char array that points to target text end (exclusive) * @param fontType font type to use for target text representation * @param x <code>'x'</code> coordinate that should be used as a starting point for target text representation. * It's necessity is implied by the fact that IDEA editor may represent tabulation symbols in any range * from <code>[1; tab size]</code> (check {@link #nextTabStop(int, Editor)} for more details) * @return width in pixels required for target text representation */ public static int textWidth(@NotNull Editor editor, @NotNull CharSequence text, int start, int end, @JdkConstants.FontStyle int fontType, int x) { int result = 0; for (int i = start; i < end; i++) { char c = text.charAt(i); if (c != '\t') { FontInfo font = fontForChar(c, fontType, editor); result += font.charWidth(c); continue; } result += nextTabStop(x + result, editor) - result - x; } return result; }
/** * Allows to answer what width in pixels is required to draw fragment of the given char array from <code>[start; end)</code> interval * at the given editor. * <p/> * Tabulation symbols is processed specially, i.e. it's ta * <p/> * <b>Note:</b> it's assumed that target text fragment remains to the single line, i.e. line feed symbols within it are not * treated specially. * * @param editor editor that will be used for target text representation * @param text target text holder * @param start offset within the given char array that points to target text start (inclusive) * @param end offset within the given char array that points to target text end (exclusive) * @param fontType font type to use for target text representation * @param x <code>'x'</code> coordinate that should be used as a starting point for target text representation. * It's necessity is implied by the fact that IDEA editor may represent tabulation symbols in any range * from <code>[1; tab size]</code> (check {@link #nextTabStop(int, Editor)} for more details) * @return width in pixels required for target text representation */ public static int textWidth(@Nonnull Editor editor, @Nonnull CharSequence text, int start, int end, @JdkConstants.FontStyle int fontType, int x) { int result = 0; for (int i = start; i < end; i++) { char c = text.charAt(i); if (c != '\t') { FontInfo font = fontForChar(c, fontType, editor); result += font.charWidth(c); continue; } result += nextTabStop(x + result, editor) - result - x; } return result; }