IDEA-115799 diff: fix changes painting over scrollbar in 3-way panel

do not eat 100% of CPU due to recursive calls of repaint() (it was not lead to deadlock, so was not fixed all this time).
This commit is contained in:
Aleksey Pivovarov
2014-10-13 16:31:32 +04:00
parent bfb5b46441
commit 0046ae2f14
6 changed files with 93 additions and 51 deletions
@@ -47,6 +47,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.util.containers.WeakList;
import com.intellij.util.ui.ButtonlessScrollBarUI;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -825,7 +826,7 @@ public class EditorWindowImpl extends UserDataHolderBase implements EditorWindow
}
@Override
public void registerScrollBarRepaintCallback(@Nullable RepaintCallback callback) {
public void registerScrollBarRepaintCallback(@Nullable ButtonlessScrollBarUI.ScrollbarRepaintCallback callback) {
myDelegate.registerScrollBarRepaintCallback(callback);
}
@@ -91,7 +91,8 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
private Animator myMacScrollbarFadeAnimator;
private double myMacScrollbarFadeLevel = 0;
private boolean myMacScrollbarHidden;
private ScrollbarRepaintCallback myRepaintCallback;
protected ButtonlessScrollBarUI() {
myAdjustmentListener = new AdjustmentListener() {
@@ -264,6 +265,43 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
}
}
/**
* This is overridden only to increase the invalid area.
* This ensures that whole track will be repainted in case of installed callback
*/
@Override
protected void setThumbBounds(int x, int y, int width, int height) {
if (myRepaintCallback == null) {
super.setThumbBounds(x, y, width, height);
}
else {
/* If the thumbs bounds haven't changed, we're done.
*/
if ((thumbRect.x == x) &&
(thumbRect.y == y) &&
(thumbRect.width == width) &&
(thumbRect.height == height)) {
return;
}
/* Update thumbRect, and repaint the union of x,y,w,h and
* the old thumbRect.
*/
int minX = Math.min(x, trackRect.x);
int minY = Math.min(y, trackRect.y);
int maxX = Math.max(x + width, trackRect.x + trackRect.width);
int maxY = Math.max(y + height, trackRect.y + trackRect.height);
thumbRect.setBounds(x, y, width, height);
scrollbar.repaint(minX, minY, maxX - minX, maxY - minY);
// Once there is API to determine the mouse location this will need
// to be changed.
setThumbRollover(false);
}
}
@Override
protected ModelListener createModelListener() {
return new ModelListener() {
@@ -615,6 +653,10 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
else {
g.drawLine(bounds.x, bounds.y, bounds.x + bounds.width, bounds.y);
}
if (myRepaintCallback != null) {
myRepaintCallback.call(g);
}
}
@Override
@@ -774,6 +816,10 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
return myMacScrollbarHidden && Registry.is("editor.distraction.free.mode");
}
public void registerRepaintCallback(ScrollbarRepaintCallback callback) {
myRepaintCallback = callback;
}
private static class EmptyButton extends JButton {
private EmptyButton() {
setFocusable(false);
@@ -795,4 +841,8 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
return getMaximumSize();
}
}
public interface ScrollbarRepaintCallback {
void call(Graphics g);
}
}
@@ -34,13 +34,13 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.plaf.ScrollBarUI;
import java.awt.*;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
/**
* The container for an {@link Editor}, which is added then to {@link com.intellij.openapi.diff.impl.util.ThreePanels}.
*/
public class EditorPlace extends JComponent implements Disposable, EditorEx.RepaintCallback {
public class EditorPlace extends JComponent implements Disposable, ButtonlessScrollBarUI.ScrollbarRepaintCallback {
private static final Logger LOG = Logger.getInstance(EditorPlace.class);
@NotNull private final MergePanel2.DiffEditorState myState;
@@ -60,49 +60,44 @@ public class EditorPlace extends JComponent implements Disposable, EditorEx.Repa
@Override
public void paint(Graphics g) {
super.paint(g);
paintThis(g);
paintThis(g, true);
}
@Override
public void call(Graphics g) {
repaintScrollbar();
paintThis(g, false);
}
private void repaintScrollbar() {
if (myEditor == null || myColumn != MergePanelColumn.BASE) {
return; // we draw above the scrollbar only in the central column
}
Component editorComponent = myEditor.getComponent();
JScrollBar scrollBar = myEditor.getScrollPane().getVerticalScrollBar();
repaint(editorComponent.getWidth() - scrollBar.getWidth(), 0, scrollBar.getWidth(), scrollBar.getHeight());
}
private void paintThis(Graphics g) {
if (myEditor != null) {
ArrayList<DividerPolygon> polygons = DividerPolygon.createVisiblePolygons(myMergePanel.getSecondEditingSide(), FragmentSide.SIDE1,
DiffDivider.MERGE_DIVIDER_POLYGONS_OFFSET);
for (DividerPolygon polygon : polygons) {
int startY = polygon.getTopLeftY();
int endY = polygon.getBottomLeftY();
int height = endY - startY;
if (height == 0) { // draw at least a one-pixel line (e.g. for insertion or deletion), as it is done in highlighters
height = 1;
}
drawPolygonAboveScrollBar((Graphics2D)g, startY, height, polygon.getColor(), polygon.isApplied());
}
}
}
private void drawPolygonAboveScrollBar(@NotNull Graphics2D g, int startY, int height, @NotNull Color color, boolean applied) {
private void paintThis(Graphics g, boolean paintOnEditor) {
if (myEditor == null) return;
// painting only above the central scrollbar, because painting on edge scrollbars is not needed, and there are error stripes
if (myColumn != MergePanelColumn.BASE) {
return;
}
if (myColumn != MergePanelColumn.BASE) return;
int offset = paintOnEditor ? DiffDivider.MERGE_DIVIDER_POLYGONS_OFFSET : DiffDivider.MERGE_SCROLL_DIVIDER_POLYGONS_OFFSET;
ArrayList<DividerPolygon> polygons = DividerPolygon.createVisiblePolygons(myMergePanel.getSecondEditingSide(), FragmentSide.SIDE1,
offset);
for (DividerPolygon polygon : polygons) {
int startY = polygon.getTopLeftY();
int endY = polygon.getBottomLeftY();
int height = endY - startY;
if (height == 0) { // draw at least a one-pixel line (e.g. for insertion or deletion), as it is done in highlighters
height = 1;
}
drawPolygonAboveScrollBar((Graphics2D)g, startY, height, polygon.getColor(), polygon.isApplied(), paintOnEditor);
}
}
private void drawPolygonAboveScrollBar(@NotNull Graphics2D g,
int startY,
int height,
@NotNull Color color,
boolean applied,
boolean paintOnEditor) {
g.setColor(color);
JScrollBar scrollBar = myEditor.getScrollPane().getVerticalScrollBar();
int startX = scrollBar.getX();
int startX = paintOnEditor ? scrollBar.getX() : 0;
int endX = startX + scrollBar.getWidth() - 1;
Rectangle thumb = calcThumbBounds(scrollBar);
@@ -249,6 +244,7 @@ public class EditorPlace extends JComponent implements Disposable, EditorEx.Repa
public interface EditorListener {
void onEditorCreated(EditorPlace place);
void onEditorReleased(Editor releasedEditor);
}
@@ -29,6 +29,7 @@ import java.awt.*;
public class DiffDivider extends JComponent {
public static final int MERGE_DIVIDER_POLYGONS_OFFSET = 1;
public static final int MERGE_SCROLL_DIVIDER_POLYGONS_OFFSET = 2;
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.util.DiffDivider");
private final Editor[] myEditors = new Editor[2];
private final FragmentSide myLeftSide;
@@ -32,6 +32,7 @@ import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ui.ButtonlessScrollBarUI;
import org.intellij.lang.annotations.MagicConstant;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -262,10 +263,5 @@ public interface EditorEx extends Editor {
* @param callback callback which will be called from the {@link javax.swing.JComponent#paint(java.awt.Graphics)} method of
* the editor vertical scrollbar.
*/
void registerScrollBarRepaintCallback(@Nullable RepaintCallback callback);
interface RepaintCallback {
void call(Graphics g);
}
void registerScrollBarRepaintCallback(@Nullable ButtonlessScrollBarUI.ScrollbarRepaintCallback callback);
}
@@ -597,7 +597,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
@Override
public void registerScrollBarRepaintCallback(@Nullable RepaintCallback callback) {
public void registerScrollBarRepaintCallback(@Nullable ButtonlessScrollBarUI.ScrollbarRepaintCallback callback) {
myVerticalScrollBar.registerRepaintCallback(callback);
}
@@ -4779,7 +4779,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
class MyScrollBar extends JBScrollBar implements IdeGlassPane.TopComponent {
@NonNls private static final String APPLE_LAF_AQUA_SCROLL_BAR_UI_CLASS = "apple.laf.AquaScrollBarUI";
private ScrollBarUI myPersistentUI;
@Nullable private RepaintCallback myRepaintCallback;
private MyScrollBar(@JdkConstants.AdjustableOrientation int orientation) {
super(orientation);
@@ -4804,9 +4803,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
@Override
public void paint(@NotNull Graphics g) {
super.paint(g);
if (myRepaintCallback != null) {
myRepaintCallback.call(g);
}
}
/**
@@ -4877,8 +4873,10 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
return myEditorComponent.getScrollableBlockIncrement(vr, SwingConstants.VERTICAL, direction);
}
public void registerRepaintCallback(@Nullable RepaintCallback callback) {
myRepaintCallback = callback;
public void registerRepaintCallback(@Nullable ButtonlessScrollBarUI.ScrollbarRepaintCallback callback) {
if (myPersistentUI instanceof ButtonlessScrollBarUI) {
((ButtonlessScrollBarUI)myPersistentUI).registerRepaintCallback(callback);
}
}
}