private int computeTextWidth(@NotNull Font font, final boolean mainTextOnly) { int result = 0; int baseSize = font.getSize(); boolean wasSmaller = false; for (int i = 0; i < myAttributes.size(); i++) { SimpleTextAttributes attributes = myAttributes.get(i); boolean isSmaller = attributes.isSmaller(); if (font.getStyle() != attributes.getFontStyle() || isSmaller != wasSmaller) { // derive font only if it is necessary font = font.deriveFont( attributes.getFontStyle(), isSmaller ? UIUtil.getFontSize(UIUtil.FontSize.SMALL) : baseSize); } wasSmaller = isSmaller; result += computeStringWidth(i, font); final int fixedWidth = myFragmentPadding.get(i); if (fixedWidth > 0 && result < fixedWidth) { result = fixedWidth; } if (mainTextOnly && myMainTextLastIndex >= 0 && i == myMainTextLastIndex) break; } return result; }
/** * Returns the index of text fragment at the specified X offset. * * @param x the offset * @return the index of the fragment, {@link #FRAGMENT_ICON} if the icon is at the offset, or -1 * if nothing is there. */ public int findFragmentAt(int x) { int curX = myIpad.left; if (myIcon != null && !myIconOnTheRight) { final int iconRight = myIcon.getIconWidth() + myIconTextGap; if (x < iconRight) { return FRAGMENT_ICON; } curX += iconRight; } Font font = getBaseFont(); int baseSize = font.getSize(); boolean wasSmaller = false; for (int i = 0; i < myAttributes.size(); i++) { SimpleTextAttributes attributes = myAttributes.get(i); boolean isSmaller = attributes.isSmaller(); if (font.getStyle() != attributes.getFontStyle() || isSmaller != wasSmaller) { // derive font only if it is necessary font = font.deriveFont( attributes.getFontStyle(), isSmaller ? UIUtil.getFontSize(UIUtil.FontSize.SMALL) : baseSize); } wasSmaller = isSmaller; final int curWidth = computeStringWidth(i, font); if (x >= curX && x < curX + curWidth) { return i; } curX += curWidth; final int fragmentPadding = myFragmentPadding.get(i); if (fragmentPadding > 0 && curX < fragmentPadding) { curX = fragmentPadding; } } if (myIcon != null && myIconOnTheRight) { curX += myIconTextGap; if (x >= curX && x < curX + myIcon.getIconWidth()) { return FRAGMENT_ICON; } } return -1; }
protected int doPaintText(Graphics2D g, int offset, boolean focusAroundIcon) { // If there is no icon, then we have to add left internal padding if (offset == 0) { offset = myIpad.left; } int textStart = offset; if (myBorder != null) { offset += myBorder.getBorderInsets(this).left; } final List<Object[]> searchMatches = new ArrayList<>(); applyAdditionalHints(g); final Font baseFont = getBaseFont(); g.setFont(baseFont); offset += computeTextAlignShift(baseFont); int baseSize = baseFont.getSize(); FontMetrics baseMetrics = g.getFontMetrics(); Rectangle area = computePaintArea(); final int textBaseline = area.y + getTextBaseLine(baseMetrics, area.height); boolean wasSmaller = false; for (int i = 0; i < myFragments.size(); i++) { final SimpleTextAttributes attributes = myAttributes.get(i); Font font = g.getFont(); boolean isSmaller = attributes.isSmaller(); if (font.getStyle() != attributes.getFontStyle() || isSmaller != wasSmaller) { // derive font only if it is necessary font = font.deriveFont( attributes.getFontStyle(), isSmaller ? UIUtil.getFontSize(UIUtil.FontSize.SMALL) : baseSize); } wasSmaller = isSmaller; g.setFont(font); final FontMetrics metrics = g.getFontMetrics(font); final int fragmentWidth = computeStringWidth(i, font); final int fragmentPadding = myFragmentPadding.get(i); final Color bgColor = attributes.isSearchMatch() ? null : attributes.getBgColor(); if ((attributes.isOpaque() || isOpaque()) && bgColor != null) { g.setColor(bgColor); g.fillRect(offset, 0, fragmentWidth, getHeight()); } Color color = attributes.getFgColor(); if (color == null) { // in case if color is not defined we have to get foreground color from Swing // hierarchy color = getForeground(); } if (!isEnabled()) { color = UIUtil.getInactiveTextColor(); } g.setColor(color); final int fragmentAlignment = myFragmentAlignment.get(i); final int endOffset; if (fragmentPadding > 0 && fragmentPadding > fragmentWidth) { endOffset = fragmentPadding; if (fragmentAlignment == SwingConstants.RIGHT || fragmentAlignment == SwingConstants.TRAILING) { offset = fragmentPadding - fragmentWidth; } } else { endOffset = offset + fragmentWidth; } if (!attributes.isSearchMatch()) { if (shouldDrawMacShadow()) { g.setColor(SHADOW_COLOR); doDrawString(g, i, offset, textBaseline + 1); } if (shouldDrawDimmed()) { color = ColorUtil.dimmer(color); } g.setColor(color); doDrawString(g, i, offset, textBaseline); } // for some reason strokeState here may be incorrect, resetting the stroke helps g.setStroke(g.getStroke()); // 1. Strikeout effect if (attributes.isStrikeout() && !attributes.isSearchMatch()) { EffectPainter.STRIKE_THROUGH.paint( g, offset, textBaseline, fragmentWidth, getCharHeight(g), font); } // 2. Waved effect if (attributes.isWaved()) { if (attributes.getWaveColor() != null) { g.setColor(attributes.getWaveColor()); } EffectPainter.WAVE_UNDERSCORE.paint( g, offset, textBaseline + 1, fragmentWidth, Math.max(2, metrics.getDescent()), font); } // 3. Underline if (attributes.isUnderline()) { EffectPainter.LINE_UNDERSCORE.paint( g, offset, textBaseline, fragmentWidth, metrics.getDescent(), font); } // 4. Bold Dotted Line if (attributes.isBoldDottedLine()) { final int dottedAt = SystemInfo.isMac ? textBaseline : textBaseline + 1; final Color lineColor = attributes.getWaveColor(); UIUtil.drawBoldDottedLine( g, offset, offset + fragmentWidth, dottedAt, bgColor, lineColor, isOpaque()); } if (attributes.isSearchMatch()) { searchMatches.add( new Object[] { offset, offset + fragmentWidth, textBaseline, myFragments.get(i), g.getFont(), attributes }); } offset = endOffset; } // Paint focus border around the text and icon (if necessary) if (myPaintFocusBorder && myBorder != null) { if (focusAroundIcon) { myBorder.paintBorder(this, g, 0, 0, getWidth(), getHeight()); } else { myBorder.paintBorder(this, g, textStart, 0, getWidth() - textStart, getHeight()); } } // draw search matches after all for (final Object[] info : searchMatches) { Integer x1 = (Integer) info[0]; Integer x2 = (Integer) info[1]; UIUtil.drawSearchMatch(g, x1, x2, getHeight()); g.setFont((Font) info[4]); Integer baseline = (Integer) info[2]; String text = (String) info[3]; if (shouldDrawMacShadow()) { g.setColor(SHADOW_COLOR); g.drawString(text, x1, baseline + 1); } g.setColor(new JBColor(Gray._50, Gray._0)); g.drawString(text, x1, baseline); if (((SimpleTextAttributes) info[5]).isStrikeout()) { EffectPainter.STRIKE_THROUGH.paint(g, x1, baseline, x2 - x1, getCharHeight(g), g.getFont()); } } return offset; }