diff: show progress bar while loading annotations

* add DiffViewer-independent progress bar
This commit is contained in:
Aleksey Pivovarov
2015-09-03 19:28:10 +03:00
parent 21b63cb1e8
commit fc28fcde97
4 changed files with 121 additions and 39 deletions
@@ -31,4 +31,9 @@ public abstract class DiffContextEx extends DiffContext {
* this can be used, if some change in request or settings was made, and we need to reload DiffRequest to apply them.
*/
public abstract void reloadDiffRequest();
/*
* Show indeterminate progress near status panel.
*/
public abstract void showProgressBar(boolean enabled);
}
@@ -56,6 +56,7 @@ import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy;
import com.intellij.ui.ColorUtil;
import com.intellij.ui.HintHint;
import com.intellij.ui.JBColor;
import com.intellij.ui.JBProgressBar;
import com.intellij.ui.LightweightHint;
import com.intellij.ui.components.panels.Wrapper;
import com.intellij.util.containers.ContainerUtil;
@@ -93,6 +94,7 @@ public abstract class DiffRequestProcessor implements Disposable {
@NotNull private final Wrapper myContentPanel;
@NotNull private final Wrapper myToolbarPanel; // TODO: allow to call 'updateToolbar' from Viewer ?
@NotNull private final Wrapper myToolbarStatusPanel;
@NotNull private final MyProgressBar myProgressBar;
@NotNull private DiffRequest myActiveRequest;
@@ -119,19 +121,17 @@ public abstract class DiffRequestProcessor implements Disposable {
// UI
myPanel = new JPanel(new BorderLayout());
myMainPanel = new MyPanel();
myContentPanel = new Wrapper();
myToolbarPanel = new Wrapper();
myToolbarPanel.setFocusable(true);
myToolbarStatusPanel = new Wrapper();
myProgressBar = new MyProgressBar();
myPanel.add(myMainPanel, BorderLayout.CENTER);
JPanel topPanel = new JPanel(new BorderLayout());
topPanel.add(myToolbarPanel, BorderLayout.CENTER);
topPanel.add(myToolbarStatusPanel, BorderLayout.EAST);
myPanel = JBUI.Panels.simplePanel(myMainPanel);
JPanel statusPanel = JBUI.Panels.simplePanel(myToolbarStatusPanel).addToLeft(myProgressBar);
JPanel topPanel = JBUI.Panels.simplePanel(myToolbarPanel).addToRight(statusPanel);
myMainPanel.add(topPanel, BorderLayout.NORTH);
myMainPanel.add(myContentPanel, BorderLayout.CENTER);
@@ -894,6 +894,26 @@ public abstract class DiffRequestProcessor implements Disposable {
}
}
private static class MyProgressBar extends JBProgressBar {
private int myProgressCount = 0;
public MyProgressBar() {
setIndeterminate(true);
setVisible(false);
}
public void startProgress() {
myProgressCount++;
setVisible(true);
}
public void stopProgress() {
myProgressCount--;
LOG.assertTrue(myProgressCount >= 0);
if (myProgressCount == 0) setVisible(false);
}
}
private class MyFocusTraversalPolicy extends IdeFocusTraversalPolicy {
@Override
public final Component getDefaultComponentImpl(final Container focusCycleRoot) {
@@ -920,6 +940,16 @@ public abstract class DiffRequestProcessor implements Disposable {
reloadRequest();
}
@Override
public void showProgressBar(boolean enabled) {
if (enabled) {
myProgressBar.startProgress();
}
else {
myProgressBar.stopProgress();
}
}
@Nullable
@Override
public Project getProject() {
@@ -15,6 +15,7 @@
*/
package com.intellij.diff.util;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
@@ -22,8 +23,10 @@ import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.EmptyRunnable;
import com.intellij.openapi.util.Ref;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import org.jetbrains.annotations.CalledInAwt;
import org.jetbrains.annotations.NotNull;
@@ -162,4 +165,46 @@ public class BackgroundTaskUtil {
return resultRef.get();
}
@CalledInAwt
@NotNull
public static ProgressIndicator executeOnPooledThread(@NotNull final Consumer<ProgressIndicator> task, @NotNull Disposable parent) {
final ModalityState modalityState = ModalityState.current();
final ProgressIndicator indicator = new EmptyProgressIndicator() {
@NotNull
@Override
public ModalityState getModalityState() {
return modalityState;
}
};
indicator.start();
final Disposable disposable = new Disposable() {
@Override
public void dispose() {
if (indicator.isRunning()) indicator.cancel();
}
};
Disposer.register(parent, disposable);
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
ProgressManager.getInstance().executeProcessUnderProgress(new Runnable() {
@Override
public void run() {
try {
task.consume(indicator);
}
finally {
indicator.stop();
Disposer.dispose(disposable);
}
}
}, indicator);
}
});
return indicator;
}
}
@@ -16,6 +16,7 @@
package com.intellij.openapi.vcs.actions;
import com.intellij.diff.DiffContext;
import com.intellij.diff.DiffContextEx;
import com.intellij.diff.DiffExtension;
import com.intellij.diff.FrameDiffTool.DiffViewer;
import com.intellij.diff.contents.DiffContent;
@@ -28,19 +29,16 @@ import com.intellij.diff.tools.util.base.DiffViewerBase;
import com.intellij.diff.tools.util.base.DiffViewerListener;
import com.intellij.diff.tools.util.side.OnesideTextDiffViewer;
import com.intellij.diff.tools.util.side.TwosideTextDiffViewer;
import com.intellij.diff.util.BackgroundTaskUtil;
import com.intellij.diff.util.Side;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.localVcs.UpToDateLineNumberProvider;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator;
import com.intellij.openapi.progress.impl.ProgressManagerImpl;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
@@ -48,7 +46,10 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.annotate.AnnotationProvider;
import com.intellij.openapi.vcs.annotate.FileAnnotation;
import com.intellij.openapi.vcs.changes.*;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ChangesUtil;
import com.intellij.openapi.vcs.changes.ContentRevision;
import com.intellij.openapi.vcs.changes.CurrentContentRevision;
import com.intellij.openapi.vcs.changes.actions.diff.ChangeDiffRequestProducer;
import com.intellij.openapi.vcs.history.VcsFileRevision;
import com.intellij.openapi.vcs.history.VcsFileRevisionEx;
@@ -59,6 +60,7 @@ import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl;
import com.intellij.openapi.vcs.impl.UpToDateLineNumberProviderImpl;
import com.intellij.openapi.vcs.impl.VcsBackgroundableActions;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import com.intellij.util.ObjectUtils;
import com.intellij.vcs.AnnotationProviderEx;
import com.intellij.vcsUtil.VcsUtil;
@@ -145,41 +147,41 @@ public class AnnotateDiffViewerAction extends DumbAwareAction {
final FileAnnotationLoader loader = createAnnotationsLoader(project, viewer.getRequest(), side);
assert loader != null;
final DiffContextEx diffContext = ObjectUtils.tryCast(viewer.getContext(), DiffContextEx.class);
markRunningProgress(viewer, side, true);
if (diffContext != null) diffContext.showProgressBar(true);
// TODO: show progress in diff viewer
// TODO: we can abort loading on DiffViewer.dispose(). But vcs can't stop gracefully anyway.
Task.Backgroundable task = new Task.Backgroundable(project, VcsBundle.message("retrieving.annotations"), true,
BackgroundFromStartOption.getInstance()) {
public void run(@NotNull ProgressIndicator indicator) {
loader.run();
}
BackgroundTaskUtil.executeOnPooledThread(new Consumer<ProgressIndicator>() {
@Override
public void onCancel() {
onSuccess();
}
@Override
public void onSuccess() {
markRunningProgress(viewer, side, false);
if (loader.getException() != null) {
AbstractVcsHelper.getInstance(myProject).showError(loader.getException(), VcsBundle.message("operation.name.annotate"));
public void consume(ProgressIndicator indicator) {
try {
loader.run();
}
if (loader.getResult() == null) return;
if (viewer.isDisposed()) return;
finally {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
if (diffContext != null) diffContext.showProgressBar(false);
markRunningProgress(viewer, side, false);
annotator.showAnnotation(viewer, side, loader.getResult());
if (loader.getException() != null) {
AbstractVcsHelper vcsHelper = AbstractVcsHelper.getInstance(viewer.getProject());
vcsHelper.showError(loader.getException(), VcsBundle.message("operation.name.annotate"));
}
if (loader.getResult() == null) return;
if (viewer.isDisposed()) return;
if (loader.shouldCache()) {
putDataToCache(viewer, side, loader.getResult());
annotator.showAnnotation(viewer, side, loader.getResult());
if (loader.shouldCache()) {
putDataToCache(viewer, side, loader.getResult());
}
}
}, indicator.getModalityState());
}
}
};
ProgressIndicator indicator = new BackgroundableProcessIndicator(task);
ProgressManagerImpl progressManager = (ProgressManagerImpl)ProgressManager.getInstance();
progressManager.runProcessWithProgressAsynchronously(task, indicator, null, ModalityState.current());
}, viewer);
}
@Nullable