diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/PresentableGoToChangePopupAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/PresentableGoToChangePopupAction.java index b4677fdb0772..d46e753778d3 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/PresentableGoToChangePopupAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/PresentableGoToChangePopupAction.java @@ -9,6 +9,8 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.JBPopupListener; +import com.intellij.openapi.ui.popup.LightweightWindowEvent; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.FilePath; @@ -51,27 +53,36 @@ public abstract class PresentableGoToChangePopupAction extends GoToChangePopu return getChanges().getList().size() > 1; } - @NotNull - private DefaultTreeModel buildTreeModel(@NotNull Project project, - @NotNull ChangesGroupingPolicyFactory grouping, - @NotNull List changes) { - MultiMap groups = MultiMap.createLinked(); + private class MyAsyncChangesTreeModel extends SimpleAsyncChangesTreeModel { + private final Project myProject; + private final List myChanges; - for (int i = 0; i < changes.size(); i++) { - PresentableChange change = getPresentation(changes.get(i)); - if (change == null) continue; - - FilePath filePath = change.getFilePath(); - FileStatus fileStatus = change.getFileStatus(); - ChangesBrowserNode.Tag tag = change.getTag(); - groups.putValue(tag, new GenericChangesBrowserNode(filePath, fileStatus, i)); + private MyAsyncChangesTreeModel(@NotNull Project project, @NotNull List changes) { + myProject = project; + myChanges = changes; } - MyTreeModelBuilder builder = new MyTreeModelBuilder(project, grouping); - for (ChangesBrowserNode.Tag tag : groups.keySet()) { - builder.setGenericNodes(groups.get(tag), tag); + @NotNull + @Override + public DefaultTreeModel buildTreeModelSync(@NotNull ChangesGroupingPolicyFactory grouping) { + MultiMap groups = MultiMap.createLinked(); + + for (int i = 0; i < myChanges.size(); i++) { + PresentableChange change = getPresentation(myChanges.get(i)); + if (change == null) continue; + + FilePath filePath = change.getFilePath(); + FileStatus fileStatus = change.getFileStatus(); + ChangesBrowserNode.Tag tag = change.getTag(); + groups.putValue(tag, new GenericChangesBrowserNode(filePath, fileStatus, i)); + } + + MyTreeModelBuilder builder = new MyTreeModelBuilder(myProject, grouping); + for (ChangesBrowserNode.Tag tag : groups.keySet()) { + builder.setGenericNodes(groups.get(tag), tag); + } + return builder.build(); } - return builder.build(); } protected abstract void onSelected(@NotNull T change); @@ -97,6 +108,12 @@ public abstract class PresentableGoToChangePopupAction extends GoToChangePopu .setCancelKeyEnabled(true) .setCancelOnClickOutside(true) .setDimensionServiceKey(project, "Diff.GoToChangePopup", false) + .addListener(new JBPopupListener() { + @Override + public void onClosed(@NotNull LightweightWindowEvent event) { + cb.shutdown(); + } + }) .createPopup()); return popup.get(); @@ -106,7 +123,7 @@ public abstract class PresentableGoToChangePopupAction extends GoToChangePopu // Helpers // - private class MyChangesBrowser extends ChangesBrowserBase { + private class MyChangesBrowser extends AsyncChangesBrowserBase { @NotNull private final Ref myRef; @NotNull private final ListSelection myChanges; @@ -117,25 +134,28 @@ public abstract class PresentableGoToChangePopupAction extends GoToChangePopu myViewer.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); init(); - myViewer.rebuildTree(); + AsyncChangesTree viewer = getViewer(); + AsyncChangesTree.RequestId requestId = viewer.requestRefresh(); if (myChanges.getSelectedIndex() != -1) { UiNotifyConnector.doWhenFirstShown(this, () -> { - DefaultMutableTreeNode toSelect = TreeUtil.findNode(myViewer.getRoot(), node -> { - return node instanceof GenericChangesBrowserNode && - ((GenericChangesBrowserNode)node).getIndex() == myChanges.getSelectedIndex(); + viewer.invokeAfterRefresh(requestId, () -> { + DefaultMutableTreeNode toSelect = TreeUtil.findNode(myViewer.getRoot(), node -> { + return node instanceof GenericChangesBrowserNode && + ((GenericChangesBrowserNode)node).getIndex() == myChanges.getSelectedIndex(); + }); + if (toSelect != null) { + TreeUtil.selectNode(myViewer, toSelect); + } }); - if (toSelect != null) { - TreeUtil.selectNode(myViewer, toSelect); - } }); } } @NotNull @Override - protected DefaultTreeModel buildTreeModel() { - return PresentableGoToChangePopupAction.this.buildTreeModel(myProject, getGrouping(), myChanges.getList()); + protected AsyncChangesTreeModel getChangesTreeModel() { + return new MyAsyncChangesTreeModel(myProject, myChanges.getList()); } @NotNull diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/savedPatches/SavedPatchesChangesBrowser.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/savedPatches/SavedPatchesChangesBrowser.kt index dbebe78f9f6f..7c45c6f4ab14 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/savedPatches/SavedPatchesChangesBrowser.kt +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/savedPatches/SavedPatchesChangesBrowser.kt @@ -23,8 +23,11 @@ import java.awt.Component import java.util.concurrent.CompletableFuture import javax.swing.tree.DefaultTreeModel -class SavedPatchesChangesBrowser(project: Project, private val focusMainUi: (Component?) -> Unit, - parentDisposable: Disposable) : ChangesBrowserBase(project, false, false), Disposable { +class SavedPatchesChangesBrowser(project: Project, + private val focusMainUi: (Component?) -> Unit, + parentDisposable: Disposable) + : AsyncChangesBrowserBase(project, false, false), Disposable { + var changes: Collection = emptyList() private set @@ -77,17 +80,19 @@ class SavedPatchesChangesBrowser(project: Project, private val focusMainUi: (Com return super.createPopupMenuActions() + ActionManager.getInstance().getAction("Vcs.SavedPatches.ChangesBrowser.ContextMenu") } - override fun buildTreeModel(): DefaultTreeModel { - val builder = TreeModelBuilder(myProject, grouping) - val groupedChanges = changes.groupBy { it.tag } - for ((tag, changes) in groupedChanges) { - if (changes.isEmpty()) continue - val root = if (tag == null) builder.myRoot else builder.createTagNode(tag, SimpleTextAttributes.REGULAR_ATTRIBUTES, false) - changes.forEach { change -> - builder.insertChangeNode(change.filePath, root, ChangeObjectNode(change)) + override val changesTreeModel: AsyncChangesTreeModel = object : SimpleAsyncChangesTreeModel() { + override fun buildTreeModelSync(grouping: ChangesGroupingPolicyFactory): DefaultTreeModel { + val builder = TreeModelBuilder(myProject, grouping) + val groupedChanges = changes.groupBy { it.tag } + for ((tag, changes) in groupedChanges) { + if (changes.isEmpty()) continue + val root = if (tag == null) builder.myRoot else builder.createTagNode(tag, SimpleTextAttributes.REGULAR_ATTRIBUTES, false) + changes.forEach { change -> + builder.insertChangeNode(change.filePath, root, ChangeObjectNode(change)) + } } + return builder.build() } - return builder.build() } private fun setEmpty(updateEmptyText: (StatusText) -> Unit) = setData(emptyList(), updateEmptyText) @@ -186,6 +191,7 @@ class SavedPatchesChangesBrowser(project: Project, private val focusMainUi: (Com } override fun dispose() { + shutdown() } private class ChangeObjectNode(change: SavedPatchesProvider.ChangeObject) : diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/SimpleChangesBrowser.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/SimpleChangesBrowser.java index 967da19e5bbf..d19b77d8313c 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/SimpleChangesBrowser.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/SimpleChangesBrowser.java @@ -10,6 +10,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.tree.DefaultTreeModel; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; public class SimpleChangesBrowser extends ChangesBrowserBase { @@ -30,8 +31,12 @@ public class SimpleChangesBrowser extends ChangesBrowserBase { } + /** + * @deprecated Consider overriding {@link ChangesBrowserBase} instead. + */ @NotNull @Override + @Deprecated protected DefaultTreeModel buildTreeModel() { return TreeModelBuilder.buildFromChanges(myProject, getGrouping(), myChanges, myChangeNodeDecorator); } diff --git a/plugins/git4idea/src/git4idea/ui/ChangesBrowserWithRollback.java b/plugins/git4idea/src/git4idea/ui/ChangesBrowserWithRollback.java index aec77d3d756d..1dd3c60d7738 100644 --- a/plugins/git4idea/src/git4idea/ui/ChangesBrowserWithRollback.java +++ b/plugins/git4idea/src/git4idea/ui/ChangesBrowserWithRollback.java @@ -9,15 +9,12 @@ import com.intellij.openapi.vcs.changes.ChangeListAdapter; import com.intellij.openapi.vcs.changes.ChangeListManager; import com.intellij.openapi.vcs.changes.RemoteRevisionsCache; import com.intellij.openapi.vcs.changes.actions.RollbackDialogAction; -import com.intellij.openapi.vcs.changes.ui.ChangesBrowserBase; -import com.intellij.openapi.vcs.changes.ui.RemoteStatusChangeNodeDecorator; -import com.intellij.openapi.vcs.changes.ui.TreeModelBuilder; +import com.intellij.openapi.vcs.changes.ui.*; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.update.DisposableUpdate; import com.intellij.util.ui.update.MergingUpdateQueue; import org.jetbrains.annotations.NotNull; -import javax.swing.tree.DefaultTreeModel; import java.util.Collection; import java.util.HashSet; import java.util.List; @@ -28,7 +25,7 @@ import java.util.Set; * After the revert completes, the changes list is automatically refreshed according to the actual changes * retrieved from the {@link ChangeListManager}. */ -public class ChangesBrowserWithRollback extends ChangesBrowserBase implements Disposable { +public class ChangesBrowserWithRollback extends AsyncChangesBrowserBase implements Disposable { private final Set myOriginalChanges; public ChangesBrowserWithRollback(@NotNull Project project, @NotNull List changes) { @@ -45,6 +42,7 @@ public class ChangesBrowserWithRollback extends ChangesBrowserBase implements Di @Override public void dispose() { + shutdown(); } @NotNull @@ -67,12 +65,14 @@ public class ChangesBrowserWithRollback extends ChangesBrowserBase implements Di @NotNull @Override - protected DefaultTreeModel buildTreeModel() { - Collection allChanges = ChangeListManager.getInstance(myProject).getAllChanges(); - List newChanges = ContainerUtil.filter(allChanges, myOriginalChanges::contains); + protected AsyncChangesTreeModel getChangesTreeModel() { + return SimpleAsyncChangesTreeModel.create(grouping -> { + Collection allChanges = ChangeListManager.getInstance(myProject).getAllChanges(); + List newChanges = ContainerUtil.filter(allChanges, myOriginalChanges::contains); - RemoteStatusChangeNodeDecorator decorator = RemoteRevisionsCache.getInstance(myProject).getChangesNodeDecorator(); - return TreeModelBuilder.buildFromChanges(myProject, getGrouping(), newChanges, decorator); + RemoteStatusChangeNodeDecorator decorator = RemoteRevisionsCache.getInstance(myProject).getChangesNodeDecorator(); + return TreeModelBuilder.buildFromChanges(myProject, grouping, newChanges, decorator); + }); }