IDEA-161827 use line metrics to align text effects
authorSergey Malenkov <sergey.malenkov@jetbrains.com>
Thu, 6 Oct 2016 18:21:54 +0000 (21:21 +0300)
committerSergey Malenkov <sergey.malenkov@jetbrains.com>
Thu, 6 Oct 2016 18:31:28 +0000 (21:31 +0300)
platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java
platform/platform-api/src/com/intellij/ui/paint/EffectPainter.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorPainter.java
platform/util/resources/misc/registry.properties

index 40531f53318c371fce1b2847a47b29695c449556..4d035cd990fbfc5a4656923a4c7232c02d3fffa9 100644 (file)
@@ -830,18 +830,18 @@ public class SimpleColoredComponent extends JComponent implements Accessible, Co
 
       // 1. Strikeout effect
       if (attributes.isStrikeout() && !attributes.isSearchMatch()) {
-        EffectPainter.STRIKE_THROUGH.paint(g, offset, textBaseline, fragmentWidth, getCharHeight(g), null);
+        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()), null);
+        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(), null);
+        EffectPainter.LINE_UNDERSCORE.paint(g, offset, textBaseline, fragmentWidth, metrics.getDescent(), font);
       }
       // 4. Bold Dotted Line
       if (attributes.isBoldDottedLine()) {
@@ -885,7 +885,7 @@ public class SimpleColoredComponent extends JComponent implements Accessible, Co
       g.drawString(text, x1, baseline);
 
       if (((SimpleTextAttributes)info[5]).isStrikeout()) {
-        EffectPainter.STRIKE_THROUGH.paint(g, x1, baseline, x2 - x1, getCharHeight(g), null);
+        EffectPainter.STRIKE_THROUGH.paint(g, x1, baseline, x2 - x1, getCharHeight(g), g.getFont());
       }
     }
     return offset;
index 0b48426310c6f8a7293d0df09591af85ee91e324..632edb25cf58775d22941170759bb1ac516ecbf9 100644 (file)
@@ -15,7 +15,6 @@
  */
 package com.intellij.ui.paint;
 
-import com.intellij.openapi.util.SystemInfo;
 import com.intellij.openapi.util.registry.Registry;
 import com.intellij.util.JBHiDPIScaledImage;
 import com.intellij.util.ui.JBUI;
@@ -24,6 +23,7 @@ import com.intellij.util.ui.UIUtil;
 import com.intellij.util.ui.WavePainter;
 
 import java.awt.*;
+import java.awt.font.LineMetrics;
 import java.awt.geom.*;
 import java.awt.image.BufferedImage;
 import java.util.concurrent.ConcurrentHashMap;
@@ -31,7 +31,7 @@ import java.util.concurrent.ConcurrentHashMap;
 /**
  * @author Sergey.Malenkov
  */
-public enum EffectPainter implements RegionPainter<Paint> {
+public enum EffectPainter implements RegionPainter<Font> {
   /**
    * @see com.intellij.openapi.editor.markup.EffectType#LINE_UNDERSCORE
    */
@@ -44,17 +44,15 @@ public enum EffectPainter implements RegionPainter<Paint> {
      * @param y      text baseline
      * @param width  text width
      * @param height available space under text
-     * @param paint  optional color patterns
+     * @param font   optional font to calculate line metrics
      */
     @Override
-    public void paint(Graphics2D g, int x, int y, int width, int height, Paint paint) {
+    public void paint(Graphics2D g, int x, int y, int width, int height, Font font) {
       if (!Registry.is("ide.text.effect.new")) {
-        if (paint != null) g.setPaint(paint);
         g.drawLine(x, y + 1, x + width, y + 1);
       }
       else if (width > 0 && height > 0) {
-        if (paint != null) g.setPaint(paint);
-        drawLineUnderscore(g, x, y, width, height, 1, this);
+        drawLineUnderscore(g, x, y, width, height, font, 1, this);
       }
     }
   },
@@ -70,18 +68,16 @@ public enum EffectPainter implements RegionPainter<Paint> {
      * @param y      text baseline
      * @param width  text width
      * @param height available space under text
-     * @param paint  optional color patterns
+     * @param font   optional font to calculate line metrics
      */
     @Override
-    public void paint(Graphics2D g, int x, int y, int width, int height, Paint paint) {
+    public void paint(Graphics2D g, int x, int y, int width, int height, Font font) {
       if (!Registry.is("ide.text.effect.new")) {
-        if (paint != null) g.setPaint(paint);
         int h = JBUI.scale(Registry.intValue("editor.bold.underline.height", 2));
         g.fillRect(x, y, width, h);
       }
       else if (width > 0 && height > 0) {
-        if (paint != null) g.setPaint(paint);
-        drawLineUnderscore(g, x, y, width, height, 2, this);
+        drawLineUnderscore(g, x, y, width, height, font, 2, this);
       }
     }
   },
@@ -97,16 +93,12 @@ public enum EffectPainter implements RegionPainter<Paint> {
      * @param y      text baseline
      * @param width  text width
      * @param height available space under text
-     * @param paint  optional color patterns
+     * @param font   optional font to calculate line metrics
      */
     @Override
-    public void paint(Graphics2D g, int x, int y, int width, int height, Paint paint) {
-      if (!Registry.is("ide.text.effect.new")) {
-        UIUtil.drawBoldDottedLine(g, x, x + width, SystemInfo.isMac ? y : y + 1, g.getColor(), (Color)paint, false);
-      }
-      else if (width > 0 && height > 0) {
-        if (paint != null) g.setPaint(paint);
-        drawLineUnderscore(g, x, y, width, height, 2, this);
+    public void paint(Graphics2D g, int x, int y, int width, int height, Font font) {
+      if (width > 0 && height > 0) {
+        drawLineUnderscore(g, x, y, width, height, font, 2, this);
       }
     }
   },
@@ -122,16 +114,15 @@ public enum EffectPainter implements RegionPainter<Paint> {
      * @param y      text baseline
      * @param width  text width
      * @param height available space under text
-     * @param paint  optional color patterns
+     * @param font   optional font to calculate line metrics
      */
     @Override
-    public void paint(Graphics2D g, int x, int y, int width, int height, Paint paint) {
+    public void paint(Graphics2D g, int x, int y, int width, int height, Font font) {
       if (!Registry.is("ide.text.effect.new")) {
-        if (paint != null) g.setPaint(paint);
         WavePainter.forColor(g.getColor()).paint(g, x, x + width, y + height);
       }
       else if (width > 0 && height > 0) {
-        Cached.WAVE_UNDERSCORE.paint(g, x, y, width, height, paint);
+        Cached.WAVE_UNDERSCORE.paint(g, x, y, width, height, null);
       }
     }
   },
@@ -147,13 +138,21 @@ public enum EffectPainter implements RegionPainter<Paint> {
      * @param y      text baseline
      * @param width  text width
      * @param height text height
-     * @param paint  optional color patterns
+     * @param font   optional font to calculate line metrics
      */
     @Override
-    public void paint(Graphics2D g, int x, int y, int width, int height, Paint paint) {
+    public void paint(Graphics2D g, int x, int y, int width, int height, Font font) {
       if (width > 0 && height > 0) {
-        if (paint != null) g.setPaint(paint);
-        drawLineCentered(g, x, y - height, width, height, 1, this);
+        if (!Registry.is("ide.text.effect.new.metrics")) {
+          drawLineCentered(g, x, y - height, width, height, 1, this);
+        }
+        else {
+          if (font == null) font = g.getFont();
+          LineMetrics metrics = font.getLineMetrics("", g.getFontRenderContext());
+          int offset = (int)(0.5 - metrics.getStrikethroughOffset());
+          int thickness = Math.max(1, (int)(0.5 + metrics.getStrikethroughThickness()));
+          drawLine(g, x, y - offset, width, thickness, this);
+        }
       }
     }
   };
@@ -162,16 +161,28 @@ public enum EffectPainter implements RegionPainter<Paint> {
     return height > 7 && Registry.is("ide.text.effect.new.scale") ? height >> 1 : 3;
   }
 
-  private static void drawLineUnderscore(Graphics2D g, int x, int y, int width, int height, int thickness, EffectPainter painter) {
-    if (height > 3) {
-      int max = getMaxHeight(height);
-      y += height - max;
-      height = max;
-      if (thickness > 1 && height > 3) {
-        thickness = JBUI.scale(thickness);
+  private static void drawLineUnderscore(Graphics2D g, int x, int y, int width, int height, Font font, int thickness,
+                                         EffectPainter painter) {
+    if (width > 0 && height > 0) {
+      if (Registry.is("ide.text.effect.new.metrics")) {
+        if (font == null) font = g.getFont();
+        LineMetrics metrics = font.getLineMetrics("", g.getFontRenderContext());
+        int offset = Math.max(1, (int)(0.5 + metrics.getUnderlineOffset()));
+        thickness = Math.max(thickness, (int)(0.5 + thickness * metrics.getUnderlineThickness()));
+        drawLine(g, x, y + offset, width, thickness, painter);
+      }
+      else {
+        if (height > 3) {
+          int max = getMaxHeight(height);
+          y += height - max;
+          height = max;
+          if (thickness > 1 && height > 3) {
+            thickness = JBUI.scale(thickness);
+          }
+        }
+        drawLineCentered(g, x, y, width, height, thickness, painter);
       }
     }
-    drawLineCentered(g, x, y, width, height, thickness, painter);
   }
 
   private static void drawLineCentered(Graphics2D g, int x, int y, int width, int height, int thickness, EffectPainter painter) {
@@ -180,6 +191,10 @@ public enum EffectPainter implements RegionPainter<Paint> {
       y += offset - (offset >> 1);
       height = thickness;
     }
+    drawLine(g, x, y, width, height, painter);
+  }
+
+  private static void drawLine(Graphics2D g, int x, int y, int width, int height, EffectPainter painter) {
     if (painter == BOLD_DOTTED_UNDERSCORE) {
       int dx = (x % height + height) % height;
       int w = width + dx;
index 870585cb01dc8ebd925e90c2109b2f0a80dacf23..83bea60533a69b8540c899b029d6d6e5faedc0d6 100644 (file)
@@ -2456,21 +2456,26 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
       }
       if (attributes != null && attributes.getEffectColor() != null) {
         int y = visibleLineToY(visibleStartLine) + getAscent() + 1;
+        g.setColor(attributes.getEffectColor());
         if (attributes.getEffectType() == EffectType.WAVE_UNDERSCORE) {
-          EffectPainter.WAVE_UNDERSCORE.paint((Graphics2D)g, end.x, y - 1, charWidth - 1, getDescent(), attributes.getEffectColor());
+          EffectPainter.WAVE_UNDERSCORE.paint((Graphics2D)g, end.x, y - 1, charWidth - 1, getDescent(),
+                                              getColorsScheme().getFont(EditorFontType.PLAIN));
         }
         else if (attributes.getEffectType() == EffectType.BOLD_DOTTED_LINE) {
-          g.setColor(getBackgroundColor(attributes));
-          EffectPainter.BOLD_DOTTED_UNDERSCORE.paint((Graphics2D)g, end.x, y - 1, charWidth - 1, getDescent(), attributes.getEffectColor());
+          EffectPainter.BOLD_DOTTED_UNDERSCORE.paint((Graphics2D)g, end.x, y - 1, charWidth - 1, getDescent(),
+                                                     getColorsScheme().getFont(EditorFontType.PLAIN));
         }
         else if (attributes.getEffectType() == EffectType.STRIKEOUT) {
-          EffectPainter.STRIKE_THROUGH.paint((Graphics2D)g, end.x, y - 1, charWidth - 1, getCharHeight(), attributes.getEffectColor());
+          EffectPainter.STRIKE_THROUGH.paint((Graphics2D)g, end.x, y - 1, charWidth - 1, getCharHeight(),
+                                             getColorsScheme().getFont(EditorFontType.PLAIN));
         }
         else if (attributes.getEffectType() == EffectType.BOLD_LINE_UNDERSCORE) {
-          EffectPainter.BOLD_LINE_UNDERSCORE.paint((Graphics2D)g, end.x, y - 1, charWidth - 1, getDescent(), attributes.getEffectColor());
+          EffectPainter.BOLD_LINE_UNDERSCORE.paint((Graphics2D)g, end.x, y - 1, charWidth - 1, getDescent(),
+                                                   getColorsScheme().getFont(EditorFontType.PLAIN));
         }
         else if (attributes.getEffectType() != EffectType.BOXED) {
-          EffectPainter.LINE_UNDERSCORE.paint((Graphics2D)g, end.x, y - 1, charWidth - 1, getDescent(), attributes.getEffectColor());
+          EffectPainter.LINE_UNDERSCORE.paint((Graphics2D)g, end.x, y - 1, charWidth - 1, getDescent(),
+                                              getColorsScheme().getFont(EditorFontType.PLAIN));
         }
       }
     }
@@ -3568,6 +3573,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
 
     if (effectColor != null) {
       final Color savedColor = g.getColor();
+      g.setColor(effectColor);
 
 //      myBorderEffect.flushIfCantProlong(g, this, effectType, effectColor);
       int xEnd = x;
@@ -3583,25 +3589,26 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
       }
 
       if (effectType == EffectType.LINE_UNDERSCORE) {
-        EffectPainter.LINE_UNDERSCORE.paint((Graphics2D)g, xStart, y, xEnd - xStart, getDescent(), effectColor);
-        g.setColor(savedColor);
+        EffectPainter.LINE_UNDERSCORE.paint((Graphics2D)g, xStart, y, xEnd - xStart, getDescent(),
+                                            getColorsScheme().getFont(EditorFontType.PLAIN));
       }
       else if (effectType == EffectType.BOLD_LINE_UNDERSCORE) {
-        EffectPainter.BOLD_LINE_UNDERSCORE.paint((Graphics2D)g, xStart, y, xEnd - xStart, getDescent(), effectColor);
-        g.setColor(savedColor);
+        EffectPainter.BOLD_LINE_UNDERSCORE.paint((Graphics2D)g, xStart, y, xEnd - xStart, getDescent(),
+                                                 getColorsScheme().getFont(EditorFontType.PLAIN));
       }
       else if (effectType == EffectType.STRIKEOUT) {
-        EffectPainter.STRIKE_THROUGH.paint((Graphics2D)g, xStart, y, xEnd - xStart, getCharHeight(), effectColor);
-        g.setColor(savedColor);
+        EffectPainter.STRIKE_THROUGH.paint((Graphics2D)g, xStart, y, xEnd - xStart, getCharHeight(),
+                                           getColorsScheme().getFont(EditorFontType.PLAIN));
       }
       else if (effectType == EffectType.WAVE_UNDERSCORE) {
-        EffectPainter.WAVE_UNDERSCORE.paint((Graphics2D)g, xStart, y, xEnd - xStart, getDescent(), effectColor);
-        g.setColor(savedColor);
+        EffectPainter.WAVE_UNDERSCORE.paint((Graphics2D)g, xStart, y, xEnd - xStart, getDescent(),
+                                            getColorsScheme().getFont(EditorFontType.PLAIN));
       }
       else if (effectType == EffectType.BOLD_DOTTED_LINE) {
-        g.setColor(getBackgroundColor());
-        EffectPainter.BOLD_DOTTED_UNDERSCORE.paint((Graphics2D)g, xStart, y, xEnd - xStart, getDescent(), effectColor);
+        EffectPainter.BOLD_DOTTED_UNDERSCORE.paint((Graphics2D)g, xStart, y, xEnd - xStart, getDescent(),
+                                                   getColorsScheme().getFont(EditorFontType.PLAIN));
       }
+      g.setColor(savedColor);
     }
 
     return x;
index 61ba33408d260b86b889a886385040aca3bfb448..986bdac24bb5bbad2282b3cd0859cc1d20d252a0 100644 (file)
@@ -431,26 +431,30 @@ class EditorPainter implements TextDrawingCallback {
   }
 
   private void paintTextEffect(Graphics2D g, float xFrom, float xTo, int y, Color effectColor, EffectType effectType, boolean allowBorder) {
+    g.setColor(effectColor);
     int xStart = (int)xFrom;
     int xEnd = (int)xTo;
     if (effectType == EffectType.LINE_UNDERSCORE) {
-      EffectPainter.LINE_UNDERSCORE.paint(g, xStart, y, xEnd - xStart, myView.getDescent(), effectColor);
+      EffectPainter.LINE_UNDERSCORE.paint(g, xStart, y, xEnd - xStart, myView.getDescent(),
+                                          myEditor.getColorsScheme().getFont(EditorFontType.PLAIN));
     }
     else if (effectType == EffectType.BOLD_LINE_UNDERSCORE) {
-      EffectPainter.BOLD_LINE_UNDERSCORE.paint(g, xStart, y, xEnd - xStart, myView.getDescent(), effectColor);
+      EffectPainter.BOLD_LINE_UNDERSCORE.paint(g, xStart, y, xEnd - xStart, myView.getDescent(),
+                                               myEditor.getColorsScheme().getFont(EditorFontType.PLAIN));
     }
     else if (effectType == EffectType.STRIKEOUT) {
-      EffectPainter.STRIKE_THROUGH.paint(g, xStart, y, xEnd - xStart, myView.getCharHeight(), effectColor);
+      EffectPainter.STRIKE_THROUGH.paint(g, xStart, y, xEnd - xStart, myView.getCharHeight(),
+                                         myEditor.getColorsScheme().getFont(EditorFontType.PLAIN));
     }
     else if (effectType == EffectType.WAVE_UNDERSCORE) {
-      EffectPainter.WAVE_UNDERSCORE.paint(g, xStart, y, xEnd - xStart, myView.getDescent(), effectColor);
+      EffectPainter.WAVE_UNDERSCORE.paint(g, xStart, y, xEnd - xStart, myView.getDescent(),
+                                          myEditor.getColorsScheme().getFont(EditorFontType.PLAIN));
     }
     else if (effectType == EffectType.BOLD_DOTTED_LINE) {
-      g.setColor(myEditor.getBackgroundColor());
-      EffectPainter.BOLD_DOTTED_UNDERSCORE.paint(g, xStart, y, xEnd - xStart, myView.getDescent(), effectColor);
+      EffectPainter.BOLD_DOTTED_UNDERSCORE.paint(g, xStart, y, xEnd - xStart, myView.getDescent(),
+                                                 myEditor.getColorsScheme().getFont(EditorFontType.PLAIN));
     }
     else if (allowBorder && (effectType == EffectType.BOXED || effectType == EffectType.ROUNDED_BOX)) {
-      g.setColor(effectColor);
       drawSimpleBorder(g, xStart, xEnd - 1, y - myView.getAscent(), effectType == EffectType.ROUNDED_BOX);
     }
   }
index 03d437856f27cd2f211ba9e1365fd24b2d5d904d..3a1526a700267fa836088bc0da68ff04df39b1e2 100644 (file)
@@ -830,6 +830,8 @@ ide.text.effect.new=true
 ide.text.effect.new.description=Enables new effect painter for text
 ide.text.effect.new.scale=true
 ide.text.effect.new.scale.description=Enables scalable effect painter for text
+ide.text.effect.new.metrics=true
+ide.text.effect.new.metrics.description=Use line metrics to calculate text offset in the effect painter
 
 ide.intellij.laf.win10.ui=false
 ide.intellij.laf.win10.ui.description=Enables Windows 10 look