From 23f0bfee45e5181effea97effcfe2ec8939db08a Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Tue, 20 Jul 2010 15:09:46 +0400 Subject: [PATCH] deferred painting, fix for uml --- .../src/com/intellij/ui/DeferredIconImpl.java | 81 +++++++++++++++---- .../src/com/intellij/ui/PaintingParent.java | 39 +++++++++ 2 files changed, 105 insertions(+), 15 deletions(-) create mode 100644 platform/platform-api/src/com/intellij/ui/PaintingParent.java diff --git a/platform/lang-impl/src/com/intellij/ui/DeferredIconImpl.java b/platform/lang-impl/src/com/intellij/ui/DeferredIconImpl.java index 7dbc4a248c92..11c207c33154 100644 --- a/platform/lang-impl/src/com/intellij/ui/DeferredIconImpl.java +++ b/platform/lang-impl/src/com/intellij/ui/DeferredIconImpl.java @@ -23,6 +23,7 @@ import com.intellij.concurrency.Job; import com.intellij.concurrency.JobUtil; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.IndexNotReadyException; +import com.intellij.openapi.util.Ref; import com.intellij.util.Alarm; import com.intellij.util.Function; import com.intellij.util.ui.EmptyIcon; @@ -66,29 +67,37 @@ public class DeferredIconImpl implements DeferredIcon { if (!myIsScheduled) { myIsScheduled = true; - final Component target; + final Ref target = new Ref(null); + final Ref paintingParent = new Ref(null); + final Ref paintingParentRec = new Ref(null); final Container list = SwingUtilities.getAncestorOfClass(JList.class, c); if (list != null) { - target = list; + target.set(list); } else { final Container tree = SwingUtilities.getAncestorOfClass(JTree.class, c); if (tree != null) { - target = tree; + target.set(tree); } else { final Container table = SwingUtilities.getAncestorOfClass(JTable.class, c); if (table != null) { - target = table; + target.set(table); } else { - target = c; + target.set(c); } } } - myLastTarget = new WeakReference(target); + Container pp = SwingUtilities.getAncestorOfClass(PaintingParent.class, c); + paintingParent.set(pp); + if (paintingParent.get() != null) { + paintingParentRec.set(((PaintingParent)pp).getChildRec(c)); + } + + myLastTarget = new WeakReference(target.get()); JobUtil.submitToJobThread(new Runnable() { public void run() { @@ -100,10 +109,23 @@ public class DeferredIconImpl implements DeferredIcon { //noinspection SSBasedInspection SwingUtilities.invokeLater(new Runnable() { public void run() { + Component actualTarget = target.get(); + if (SwingUtilities.getWindowAncestor(actualTarget) == null) { + actualTarget = paintingParent.get(); + if (actualTarget == null || SwingUtilities.getWindowAncestor(actualTarget) == null) { + actualTarget = null; + myLastTarget = null; + } + } + + if (actualTarget == null) return; + + myLastTarget = new WeakReference(actualTarget); + if (shouldRevalidate) { // revalidate will not work: jtree caches size of nodes - if (target instanceof JTree) { - final TreeUI ui = ((JTree)target).getUI(); + if (actualTarget instanceof JTree) { + final TreeUI ui = ((JTree)actualTarget).getUI(); if (ui instanceof BasicTreeUI) { // this call is "fake" and only need to reset tree layout cache ((BasicTreeUI)ui).setLeftChildIndent(((Integer)UIManager.get("Tree.leftChildIndent")).intValue()); @@ -111,11 +133,16 @@ public class DeferredIconImpl implements DeferredIcon { } } - if (c == target) { + if (c == actualTarget) { c.repaint(x, y, getIconWidth(), getIconHeight()); } else { - ourRepaintScheduler.pushDirtyComponent(target); + Rectangle rec = null; + if (paintingParentRec.get() != null) { + rec = paintingParentRec.get(); + } + + ourRepaintScheduler.pushDirtyComponent(actualTarget, rec); } } }); @@ -191,20 +218,44 @@ public class DeferredIconImpl implements DeferredIcon { private static class RepaintScheduler { private final Alarm myAlarm = new Alarm(); - private final Set myQueue = new LinkedHashSet(); + private final Set myQueue = new LinkedHashSet(); - public void pushDirtyComponent(Component c) { + public void pushDirtyComponent(final Component c, final Rectangle rec) { myAlarm.cancelAllRequests(); myAlarm.addRequest(new Runnable() { public void run() { - for (Component component : myQueue) { - component.repaint(); + for (RepaintRequest each : myQueue) { + Rectangle r = each.getRectangle(); + if (r != null) { + each.getComponent().repaint(r.x, r.y, r.width, r.height); + } else { + each.getComponent().repaint(); + } } myQueue.clear(); } }, 50); - myQueue.add(c); + myQueue.add(new RepaintRequest(c, rec)); } } + + private static class RepaintRequest { + private Component myComponent; + private Rectangle myRectangle; + + private RepaintRequest(Component component, Rectangle rectangle) { + myComponent = component; + myRectangle = rectangle; + } + + public Component getComponent() { + return myComponent; + } + + public Rectangle getRectangle() { + return myRectangle; + } + } + } diff --git a/platform/platform-api/src/com/intellij/ui/PaintingParent.java b/platform/platform-api/src/com/intellij/ui/PaintingParent.java new file mode 100644 index 000000000000..a49f1235e11f --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/PaintingParent.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2010 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.ui; + +import org.jetbrains.annotations.Nullable; + +import java.awt.*; + +/** + * Used for deferred re-painting (i.e. for deferred icons). As a paint() method is invoked through the normal AWT painting cycle, deferred painting + * of children may be queued. It means that actual data used for painting is pushed for calculation and, as it gets ready, there is need for repaint. + * The target component for further repaint() is either the component that was originally exposed to painting or, if it's no longer showing (in case of a flyweight + * renderer) -- the first component up in the hierarchy that implements PaintingParent. + * + */ +public interface PaintingParent { + + /** + * Returns rectangle of a child component for further repainting + * @param c + * @return a rectange, if null -- the whole component will be repainted + */ + @Nullable + Rectangle getChildRec(Component c); + +}