From 52b198060a8613e718e91d6c4e693036bace03dd Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Wed, 2 Sep 2015 13:01:40 +0300 Subject: [PATCH] IDEA-144638 the code coverage gutter indicator overlaps with gutter icon and is impossible to click --- .../editor/ex/EditorGutterComponentEx.java | 2 + .../impl/EditorGutterComponentImpl.java | 90 +++++++++++-------- .../editor/markup/LineMarkerRendererEx.java | 30 +++++++ .../coverage/CoverageLineMarkerRenderer.java | 33 +++++-- 4 files changed, 115 insertions(+), 40 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/openapi/editor/markup/LineMarkerRendererEx.java diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorGutterComponentEx.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorGutterComponentEx.java index 4926fc4b9e2c..38c8290cea6f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorGutterComponentEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorGutterComponentEx.java @@ -52,6 +52,8 @@ public abstract class EditorGutterComponentEx extends JComponent implements Edit public abstract int getLineMarkerAreaOffset(); + public abstract int getIconAreaOffset(); + public abstract int getLineMarkerFreePaintersAreaOffset(); public abstract int getIconsAreaWidth(); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java index 7865d0877b9c..2cca0857a059 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java @@ -82,8 +82,9 @@ import java.util.List; class EditorGutterComponentImpl extends EditorGutterComponentEx implements MouseListener, MouseMotionListener, DataProvider { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.EditorGutterComponentImpl"); private static final int START_ICON_AREA_WIDTH = 15; - private static final int FREE_PAINTERS_AREA_WIDTH = 5; - private static final int GAP_BETWEEN_ICONS_AND_FREE_PAINTERS_AREA = 5; + private static final int FREE_PAINTERS_LEFT_AREA_WIDTH = 8; + private static final int FREE_PAINTERS_RIGHT_AREA_WIDTH = 5; + private static final int GAP_BETWEEN_ICONS_AND_RIGHT_FREE_PAINTERS_AREA = 5; private static final int GAP_BETWEEN_ICONS = 3; private static final int GAP_BEFORE_LINE_NUMBERS = 5; private static final int GAP_AFTER_LINE_NUMBERS = 4; @@ -97,7 +98,6 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse private final EditorImpl myEditor; private final FoldingAnchorsOverlayStrategy myAnchorsDisplayStrategy; - private int myLineMarkerAreaWidth = START_ICON_AREA_WIDTH + GAP_BETWEEN_ICONS_AND_FREE_PAINTERS_AREA + FREE_PAINTERS_AREA_WIDTH; private int myIconsAreaWidth = START_ICON_AREA_WIDTH; private int myLineNumberAreaWidth = 0; private int myAdditionalLineNumberAreaWidth = 0; @@ -116,6 +116,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse @Nullable private ActionGroup myCustomGutterPopupGroup; private TIntObjectHashMap myTextFgColors = new TIntObjectHashMap(); private boolean myPaintBackground = true; + private boolean myLeftFreePaintersAreaShown; @SuppressWarnings("unchecked") public EditorGutterComponentImpl(EditorImpl editor) { @@ -629,14 +630,14 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse private void updateSizeInner(boolean onLayout) { if (!onLayout) { calcLineNumberAreaWidth(); - calcIconAreaWidth(); + calcLineMarkerAreaWidth(); calcAnnotationsSize(); } calcAnnotationExtraSize(); } private int sizeHash() { - int result = myLineMarkerAreaWidth; + int result = getLineMarkerAreaWidth(); result = 31 * result + myTextAnnotationGuttersSize; result = 31 * result + myTextAnnotationExtraSize; result = 31 * result + getLineNumberAreaWidth(); @@ -682,19 +683,27 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse int width = editorLocationX + editorComponent.getWidth(); if (rightMarginX < width && editorLocationX < width - rightMarginX) { - int centeredSize = (width - rightMarginX - editorLocationX) / 2 - (myLineMarkerAreaWidth + getLineNumberAreaWidth()); + int centeredSize = (width - rightMarginX - editorLocationX) / 2 - (getLineMarkerAreaWidth() + getLineNumberAreaWidth()); myTextAnnotationExtraSize = Math.max(0, centeredSize - myTextAnnotationGuttersSize); } } private TIntObjectHashMap> myLineToGutterRenderers; - private void calcIconAreaWidth() { + private void calcLineMarkerAreaWidth() { myLineToGutterRenderers = new TIntObjectHashMap>(); + myLeftFreePaintersAreaShown = false; processRangeHighlighters(0, myEditor.getDocument().getTextLength(), new RangeHighlighterProcessor() { @Override public void process(@NotNull RangeHighlighter highlighter) { + LineMarkerRenderer lineMarkerRenderer = highlighter.getLineMarkerRenderer(); + if (lineMarkerRenderer instanceof LineMarkerRendererEx && + ((LineMarkerRendererEx)lineMarkerRenderer).getPosition() == LineMarkerRendererEx.Position.LEFT && + isLineMarkerVisible(highlighter)) { + myLeftFreePaintersAreaShown = true; + } + GutterMark renderer = highlighter.getGutterIconRenderer(); if (renderer == null) { return; @@ -734,8 +743,6 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse return true; } }); - - myLineMarkerAreaWidth = myIconsAreaWidth + GAP_BETWEEN_ICONS_AND_FREE_PAINTERS_AREA + FREE_PAINTERS_AREA_WIDTH; } private boolean isHighlighterVisible(RangeHighlighter highlighter) { @@ -791,28 +798,30 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse } private void paintLineMarkerRenderer(RangeHighlighter highlighter, Graphics g) { - Rectangle rectangle = getLineRendererRectangle(highlighter); - - if (rectangle != null) { - final LineMarkerRenderer lineMarkerRenderer = highlighter.getLineMarkerRenderer(); - assert lineMarkerRenderer != null; - lineMarkerRenderer.paint(myEditor, g, rectangle); + LineMarkerRenderer lineMarkerRenderer = highlighter.getLineMarkerRenderer(); + if (lineMarkerRenderer != null) { + Rectangle rectangle = getLineRendererRectangle(highlighter); + if (rectangle != null) { + lineMarkerRenderer.paint(myEditor, g, rectangle); + } } } - - @Nullable - private Rectangle getLineRendererRectangle(RangeHighlighter highlighter) { - LineMarkerRenderer renderer = highlighter.getLineMarkerRenderer(); - if (renderer == null) return null; - + + private boolean isLineMarkerVisible(RangeHighlighter highlighter) { int startOffset = highlighter.getStartOffset(); int endOffset = highlighter.getEndOffset(); FoldRegion startFoldRegion = myEditor.getFoldingModel().getCollapsedRegionAtOffset(startOffset); FoldRegion endFoldRegion = myEditor.getFoldingModel().getCollapsedRegionAtOffset(endOffset); - if (startFoldRegion != null && endFoldRegion != null && startFoldRegion.equals(endFoldRegion)) { - return null; - } + return startFoldRegion == null || endFoldRegion == null || !startFoldRegion.equals(endFoldRegion); + } + + @Nullable + private Rectangle getLineRendererRectangle(RangeHighlighter highlighter) { + if (!isLineMarkerVisible(highlighter)) return null; + + int startOffset = highlighter.getStartOffset(); + int endOffset = highlighter.getEndOffset(); int startY = myEditor.visualPositionToXY(myEditor.offsetToVisualPosition(startOffset)).y; @@ -825,9 +834,13 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse endY += myEditor.getLineHeight(); } + LineMarkerRenderer renderer = highlighter.getLineMarkerRenderer(); + boolean leftPosition = renderer instanceof LineMarkerRendererEx && + ((LineMarkerRendererEx)renderer).getPosition() == LineMarkerRendererEx.Position.LEFT; + int height = endY - startY; - int w = FREE_PAINTERS_AREA_WIDTH; - int x = getLineMarkerFreePaintersAreaOffset() - 1; + int w = leftPosition ? FREE_PAINTERS_LEFT_AREA_WIDTH : FREE_PAINTERS_RIGHT_AREA_WIDTH; + int x = leftPosition ? getLineMarkerAreaOffset() : getLineMarkerFreePaintersAreaOffset() - 1; return new Rectangle(x, startY, w, height); } @@ -845,7 +858,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse private void processIconsRow(int line, List row, LineGutterIconRendererProcessor processor) { int middleCount = 0; int middleSize = 0; - int x = getLineMarkerAreaOffset() + 2; + int x = getIconAreaOffset() + 2; final int y = myEditor.logicalPositionToXY(new LogicalPosition(line, 0)).y; for (GutterMark r : row) { @@ -862,9 +875,9 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse } } - final int leftSize = x - getLineMarkerAreaOffset(); + final int leftSize = x - getIconAreaOffset(); - x = getLineMarkerAreaOffset() + myIconsAreaWidth - 2; // because of 2px LineMarkerRenderers + x = getIconAreaOffset() + myIconsAreaWidth - 2; // because of 2px LineMarkerRenderers for (GutterMark r : row) { if (!checkDumbAware(r, myEditor.getProject())) continue; if (((GutterIconRenderer)r).getAlignment() == GutterIconRenderer.Alignment.RIGHT) { @@ -875,11 +888,11 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse } } - int rightSize = myIconsAreaWidth + getLineMarkerAreaOffset() - x + 1; + int rightSize = myIconsAreaWidth + getIconAreaOffset() - x + 1; if (middleCount > 0) { middleSize -= GAP_BETWEEN_ICONS; - x = getLineMarkerAreaOffset() + leftSize + (myIconsAreaWidth - leftSize - rightSize - middleSize) / 2; + x = getIconAreaOffset() + leftSize + (myIconsAreaWidth - leftSize - rightSize - middleSize) / 2; for (GutterMark r : row) { if (!checkDumbAware(r, myEditor.getProject())) continue; if (((GutterIconRenderer)r).getAlignment() == GutterIconRenderer.Alignment.CENTER) { @@ -1121,7 +1134,9 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse } public int getLineMarkerAreaWidth() { - return isLineMarkersShown() ? myLineMarkerAreaWidth : 0; + return isLineMarkersShown() ? ((myLeftFreePaintersAreaShown ? FREE_PAINTERS_LEFT_AREA_WIDTH : 0) + + myIconsAreaWidth + GAP_BETWEEN_ICONS_AND_RIGHT_FREE_PAINTERS_AREA + FREE_PAINTERS_RIGHT_AREA_WIDTH) : + 0; } public void setLineNumberAreaWidthFunction(@NotNull TIntFunction calculator) { @@ -1200,9 +1215,14 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse return getAnnotationsAreaOffset() + getAnnotationsAreaWidthEx(); } + @Override + public int getIconAreaOffset() { + return getLineMarkerAreaOffset() + (myLeftFreePaintersAreaShown ? FREE_PAINTERS_LEFT_AREA_WIDTH : 0); + } + @Override public int getLineMarkerFreePaintersAreaOffset() { - return getLineMarkerAreaOffset() + myIconsAreaWidth + GAP_BETWEEN_ICONS_AND_FREE_PAINTERS_AREA; + return getIconAreaOffset() + myIconsAreaWidth + GAP_BETWEEN_ICONS_AND_RIGHT_FREE_PAINTERS_AREA; } @Override @@ -1466,7 +1486,8 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse processRangeHighlighters(firstVisibleOffset, lastVisibleOffset, new RangeHighlighterProcessor() { @Override public void process(@NotNull RangeHighlighter highlighter) { - if (gutterRenderer[0] != null) return; + LineMarkerRenderer renderer = highlighter.getLineMarkerRenderer(); + if (renderer == null || gutterRenderer[0] != null) return; Rectangle rectangle = getLineRendererRectangle(highlighter); if (rectangle == null) return; @@ -1477,7 +1498,6 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse } if (startY < e.getY() && e.getY() <= endY) { - final LineMarkerRenderer renderer = highlighter.getLineMarkerRenderer(); if (renderer instanceof ActiveGutterRenderer && ((ActiveGutterRenderer)renderer).canDoAction(e)) { gutterRenderer[0] = (ActiveGutterRenderer)renderer; } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/markup/LineMarkerRendererEx.java b/platform/platform-impl/src/com/intellij/openapi/editor/markup/LineMarkerRendererEx.java new file mode 100644 index 000000000000..51f830b56c43 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/editor/markup/LineMarkerRendererEx.java @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2015 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.editor.markup; + +import com.intellij.openapi.editor.Editor; + +import java.awt.*; + +public interface LineMarkerRendererEx extends LineMarkerRenderer { + enum Position {LEFT, RIGHT} + + /** + * Determines whether line marker should be rendered to the left or to the right of icon area in gutter. + * Corresponding rectangle will be passed to renderer in {@link #paint(Editor, Graphics, Rectangle)} method. + */ + Position getPosition(); +} diff --git a/plugins/coverage-common/src/com/intellij/coverage/CoverageLineMarkerRenderer.java b/plugins/coverage-common/src/com/intellij/coverage/CoverageLineMarkerRenderer.java index e25e82570cd9..190c35bf46d3 100644 --- a/plugins/coverage-common/src/com/intellij/coverage/CoverageLineMarkerRenderer.java +++ b/plugins/coverage-common/src/com/intellij/coverage/CoverageLineMarkerRenderer.java @@ -1,5 +1,17 @@ /* - * Copyright (c) 2000-2006 JetBrains s.r.o. All Rights Reserved. + * Copyright 2000-2015 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package com.intellij.coverage; @@ -25,6 +37,7 @@ import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.editor.ex.EditorGutterComponentEx; import com.intellij.openapi.editor.impl.EditorImpl; import com.intellij.openapi.editor.markup.ActiveGutterRenderer; +import com.intellij.openapi.editor.markup.LineMarkerRendererEx; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.SearchableConfigurable; @@ -57,7 +70,7 @@ import java.util.TreeMap; /** * @author ven */ -public class CoverageLineMarkerRenderer implements ActiveGutterRenderer { +public class CoverageLineMarkerRenderer implements LineMarkerRendererEx, ActiveGutterRenderer { private static final int THICKNESS = 8; private final TextAttributesKey myKey; private final String myClassName; @@ -97,10 +110,10 @@ public class CoverageLineMarkerRenderer implements ActiveGutterRenderer { if (bgColor != null) { g.setColor(bgColor); } - g.fillRect(0, r.y, THICKNESS, r.height); + g.fillRect(r.x, r.y, r.width, r.height); final LineData lineData = getLineData(editor.xyToLogicalPosition(new Point(0, r.y)).line); if (lineData != null && lineData.isCoveredByOneTest()) { - g.drawImage( ImageLoader.loadFromResource("/gutter/unique.png"), 0, r.y, 8, 8, editor.getComponent()); + g.drawImage( ImageLoader.loadFromResource("/gutter/unique.png"), r.x, r.y, 8, 8, editor.getComponent()); } } @@ -135,7 +148,12 @@ public class CoverageLineMarkerRenderer implements ActiveGutterRenderer { } public boolean canDoAction(final MouseEvent e) { - return e.getX() < THICKNESS; + Component component = e.getComponent(); + if (component instanceof EditorGutterComponentEx) { + EditorGutterComponentEx gutter = (EditorGutterComponentEx)component; + return e.getX() > gutter.getLineMarkerAreaOffset() && e.getX() < gutter.getIconAreaOffset(); + } + return false; } public void doAction(final Editor editor, final MouseEvent e) { @@ -253,6 +271,11 @@ public class CoverageLineMarkerRenderer implements ActiveGutterRenderer { return editor.getColorsScheme().getAttributes(myKey).getErrorStripeColor(); } + @Override + public Position getPosition() { + return Position.LEFT; + } + private class GotoPreviousCoveredLineAction extends BaseGotoCoveredLineAction { public GotoPreviousCoveredLineAction(final Editor editor, final int lineNumber) {