From 0046ae2f14e964e009aefd8466ef1378011eb32a Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Fri, 10 Oct 2014 18:54:15 +0400 Subject: [PATCH] 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). --- .../injected/editor/EditorWindowImpl.java | 3 +- .../util/ui/ButtonlessScrollBarUI.java | 52 +++++++++++++- .../impl/incrementalMerge/ui/EditorPlace.java | 68 +++++++++---------- .../openapi/diff/impl/util/DiffDivider.java | 1 + .../intellij/openapi/editor/ex/EditorEx.java | 8 +-- .../openapi/editor/impl/EditorImpl.java | 12 ++-- 6 files changed, 93 insertions(+), 51 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindowImpl.java b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindowImpl.java index 077c41e477ff..10bf53bce6bb 100644 --- a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindowImpl.java +++ b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindowImpl.java @@ -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); } diff --git a/platform/platform-api/src/com/intellij/util/ui/ButtonlessScrollBarUI.java b/platform/platform-api/src/com/intellij/util/ui/ButtonlessScrollBarUI.java index 9cd99e474c65..a816efde21c5 100644 --- a/platform/platform-api/src/com/intellij/util/ui/ButtonlessScrollBarUI.java +++ b/platform/platform-api/src/com/intellij/util/ui/ButtonlessScrollBarUI.java @@ -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); + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java index 7e24e92c5cf1..1d99141ca4bb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java @@ -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 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 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); } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/DiffDivider.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/DiffDivider.java index e9c3168e401c..010aa8d7c6bd 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/DiffDivider.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/DiffDivider.java @@ -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; diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java index 62064a0b1a49..8752e13681b8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java @@ -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); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index c8f01ad58e60..7e803c59438c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -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); + } } }