protected int doPaintTextAndIcons()

in flutter-idea/src/io/flutter/view/MultiIconSimpleColoredComponent.java [789:956]


  protected int doPaintTextAndIcons(Graphics2D g, boolean focusAroundIcon) {
    float offset = myIpad.left;
    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();
    final int baseSize = baseFont.getSize();
    final FontMetrics baseMetrics = g.getFontMetrics();
    final Rectangle area = computePaintArea();
    final int textBaseline = area.y + getTextBaseLine(baseMetrics, area.height);
    boolean wasSmaller = false;
    assert (myFragments.size() == myAttributes.size());
    int i = 0;
    int iconIndex = 0;
    while (true) {
      // Go through all icons up to attribute i.
      while (iconIndex < myIcons.size() && myIcons.get(iconIndex).index <= i) {
        final Icon icon = myIcons.get(iconIndex).icon;
        final int iconWidth = icon.getIconWidth() + myIconTextGap;
        doPaintIcon(g, icon, (int)offset);
        offset += iconWidth + myIconTextGap;
        iconIndex++;
      }

      if (i >= myFragments.size()) {
        break;
      }

      final SimpleTextAttributes attributes = myAttributes.get(i);

      Font font = g.getFont();
      final 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 float 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((int)offset, 0, (int)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 float 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, (int)offset, textBaseline, (int)fragmentWidth, getCharHeight(g), font);
      }
      // 2. Waved effect
      if (attributes.isWaved()) {
        if (attributes.getWaveColor() != null) {
          g.setColor(attributes.getWaveColor());
        }
        EffectPainter.WAVE_UNDERSCORE.paint(g, (int)offset, textBaseline + 1, (int)fragmentWidth, Math.max(2, metrics.getDescent()), font);
      }
      // 3. Underline
      if (attributes.isUnderline()) {
        EffectPainter.LINE_UNDERSCORE.paint(g, (int)offset, textBaseline, (int)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, (int)offset, (int)(offset + fragmentWidth), dottedAt, bgColor, lineColor, isOpaque());
      }

      if (attributes.isSearchMatch()) {
        searchMatches.add(new Object[]{offset, offset + fragmentWidth, (float)textBaseline, myFragments.get(i), g.getFont(), attributes});
      }

      offset = endOffset;
      i++;
    }

    // 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 {
        int textStart = 0;
        // Skip all icons that occur before any text.
        for (PositionedIcon positionedIcon : myIcons) {
          if (positionedIcon.index != 0) {
            break;
          }
          textStart += positionedIcon.icon.getIconWidth() + myIconTextGap;
        }

        myBorder.paintBorder(this, g, textStart, 0, getWidth() - textStart, getHeight());
      }
    }

    // draw search matches after all
    for (final Object[] info : searchMatches) {
      final float x1 = (float)info[0];
      final float x2 = (float)info[1];
      UIUtil.drawSearchMatch(g, x1, x2, getHeight());
      g.setFont((Font)info[4]);

      final float baseline = (float)info[2];
      final 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, (int)x1, (int)baseline, (int)(x2 - x1), getCharHeight(g), g.getFont());
      }
    }
    return (int)offset;
  }