mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Build output view: progress animation fix
This commit is contained in:
@@ -17,6 +17,7 @@ package com.intellij.build;
|
||||
|
||||
import com.intellij.build.events.*;
|
||||
import com.intellij.build.events.impl.FailureImpl;
|
||||
import com.intellij.concurrency.JobScheduler;
|
||||
import com.intellij.execution.filters.Filter;
|
||||
import com.intellij.execution.filters.HyperlinkInfo;
|
||||
import com.intellij.execution.filters.TextConsoleBuilderFactory;
|
||||
@@ -46,6 +47,7 @@ import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.text.DateFormatUtil;
|
||||
import com.intellij.util.ui.ColumnInfo;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.intellij.util.ui.update.Update;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -63,6 +65,8 @@ import javax.swing.tree.TreePath;
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -77,12 +81,14 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon
|
||||
private final SimpleTreeBuilder myBuilder;
|
||||
private final Map<Object, ExecutionNode> nodesMap = ContainerUtil.newConcurrentMap();
|
||||
private final ExecutionNodeProgressAnimator myProgressAnimator;
|
||||
private Set<Update> myRequests = Collections.synchronizedSet(new HashSet<Update>());
|
||||
|
||||
private final Project myProject;
|
||||
private final SimpleTreeStructure myTreeStructure;
|
||||
private final DetailsHandler myDetailsHandler;
|
||||
private final TableColumn myTimeColumn;
|
||||
private volatile int myTimeColumnWidth;
|
||||
private final AtomicBoolean myDisposed = new AtomicBoolean();
|
||||
|
||||
public BuildTreeConsoleView(Project project) {
|
||||
myProject = project;
|
||||
@@ -104,7 +110,7 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon
|
||||
}
|
||||
}
|
||||
};
|
||||
final ExecutionNode rootNode = new ExecutionNode(myProject, null);
|
||||
final ExecutionNode rootNode = new ExecutionNode(myProject);
|
||||
rootNode.setAutoExpandNode(true);
|
||||
final ListTreeTableModelOnColumns model = new ListTreeTableModelOnColumns(new DefaultMutableTreeNode(rootNode), COLUMNS);
|
||||
|
||||
@@ -180,9 +186,6 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon
|
||||
Disposer.register(this, myBuilder);
|
||||
myBuilder.initRootNode();
|
||||
myBuilder.updateFromRoot();
|
||||
myBuilder.expand(rootNode, null);
|
||||
|
||||
myProgressAnimator = new ExecutionNodeProgressAnimator(myBuilder);
|
||||
|
||||
JPanel myContentPanel = new JPanel();
|
||||
myContentPanel.setLayout(new CardLayout());
|
||||
@@ -209,6 +212,8 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon
|
||||
myDetailsHandler = new DetailsHandler(myProject, tree, myThreeComponentsSplitter);
|
||||
myThreeComponentsSplitter.setLastComponent(myDetailsHandler.getComponent());
|
||||
myPanel.add(myThreeComponentsSplitter, BorderLayout.CENTER);
|
||||
|
||||
myProgressAnimator = new ExecutionNodeProgressAnimator(this);
|
||||
}
|
||||
|
||||
private ExecutionNode getRootElement() {
|
||||
@@ -297,6 +302,11 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
myDisposed.set(true);
|
||||
}
|
||||
|
||||
public boolean isDisposed() {
|
||||
return myDisposed.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -306,7 +316,7 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon
|
||||
if (event instanceof StartEvent) {
|
||||
ExecutionNode rootElement = getRootElement();
|
||||
if (currentNode == null) {
|
||||
currentNode = event instanceof StartBuildEvent ? rootElement : new ExecutionNode(myProject, myProgressAnimator);
|
||||
currentNode = event instanceof StartBuildEvent ? rootElement : new ExecutionNode(myProject);
|
||||
currentNode.setAutoExpandNode(currentNode == rootElement || parentNode == rootElement);
|
||||
nodesMap.put(event.getId(), currentNode);
|
||||
}
|
||||
@@ -322,12 +332,13 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon
|
||||
String buildTitle = ((StartBuildEvent)event).getBuildTitle();
|
||||
currentNode.setTitle(buildTitle);
|
||||
currentNode.setAutoExpandNode(true);
|
||||
myProgressAnimator.startMovie();
|
||||
}
|
||||
}
|
||||
else {
|
||||
currentNode = nodesMap.get(event.getId());
|
||||
if (currentNode == null && event instanceof ProgressBuildEvent) {
|
||||
currentNode = new ExecutionNode(myProject, myProgressAnimator);
|
||||
currentNode = new ExecutionNode(myProject);
|
||||
nodesMap.put(event.getId(), currentNode);
|
||||
if (parentNode != null) {
|
||||
parentNode.add(currentNode);
|
||||
@@ -354,8 +365,12 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon
|
||||
myTimeColumnWidth = timeColumnWidth;
|
||||
}
|
||||
}
|
||||
|
||||
myBuilder.queueUpdateFrom(currentNode, false, false);
|
||||
else {
|
||||
scheduleUpdate(currentNode);
|
||||
if (event instanceof StartEvent) {
|
||||
myProgressAnimator.addNode(currentNode);
|
||||
}
|
||||
}
|
||||
|
||||
if (event instanceof FinishBuildEvent) {
|
||||
String aHint = event.getHint();
|
||||
@@ -371,7 +386,7 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon
|
||||
JTree tree = myBuilder.getTree();
|
||||
if (tree != null && !tree.isRootVisible()) {
|
||||
ExecutionNode rootElement = getRootElement();
|
||||
ExecutionNode resultNode = new ExecutionNode(myProject, null);
|
||||
ExecutionNode resultNode = new ExecutionNode(myProject);
|
||||
resultNode.setName(StringUtil.toTitleCase(rootElement.getName()));
|
||||
resultNode.setHint(rootElement.getHint());
|
||||
resultNode.setEndTime(rootElement.getEndTime());
|
||||
@@ -379,9 +394,25 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon
|
||||
resultNode.setResult(rootElement.getResult());
|
||||
resultNode.setTooltip(rootElement.getTooltip());
|
||||
rootElement.add(resultNode);
|
||||
myBuilder.queueUpdateFrom(resultNode, false, false);
|
||||
|
||||
scheduleUpdate(resultNode);
|
||||
}
|
||||
}
|
||||
myProgressAnimator.stopMovie();
|
||||
myBuilder.updateFromRoot();
|
||||
}
|
||||
}
|
||||
|
||||
void scheduleUpdate(ExecutionNode executionNode) {
|
||||
final Update update = new Update(executionNode) {
|
||||
@Override
|
||||
public void run() {
|
||||
myRequests.remove(this);
|
||||
myBuilder.queueUpdateFrom(executionNode, false, true);
|
||||
}
|
||||
};
|
||||
if (myRequests.add(update)) {
|
||||
JobScheduler.getScheduler().schedule(update, 100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,6 @@ import java.util.List;
|
||||
*/
|
||||
public class ExecutionNode extends CachingSimpleNode {
|
||||
private final List<ExecutionNode> myChildrenList = ContainerUtil.newSmartList();
|
||||
@Nullable
|
||||
private final ExecutionNodeProgressAnimator myAnimator;
|
||||
private long startTime;
|
||||
private long endTime;
|
||||
@Nullable
|
||||
@@ -52,9 +50,8 @@ public class ExecutionNode extends CachingSimpleNode {
|
||||
private EventResult myResult;
|
||||
private boolean myAutoExpandNode;
|
||||
|
||||
public ExecutionNode(Project aProject, @Nullable ExecutionNodeProgressAnimator animator) {
|
||||
public ExecutionNode(Project aProject) {
|
||||
super(aProject, null);
|
||||
myAnimator = animator;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -84,15 +81,6 @@ public class ExecutionNode extends CachingSimpleNode {
|
||||
if (tooltip != null) {
|
||||
presentation.setTooltip(tooltip);
|
||||
}
|
||||
|
||||
if (myAnimator != null) {
|
||||
if (isRunning()) {
|
||||
myAnimator.addNode(this);
|
||||
}
|
||||
else {
|
||||
myAnimator.removeNode(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,25 +16,22 @@
|
||||
package com.intellij.build;
|
||||
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.ide.util.treeView.AbstractTreeBuilder;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.util.Alarm;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import com.intellij.util.SmartList;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Vladislav.Soroka
|
||||
*/
|
||||
public class ExecutionNodeProgressAnimator implements Runnable, Disposable {
|
||||
private static final int FRAMES_COUNT = 8;
|
||||
private static final int MOVIE_TIME = 1600;
|
||||
private static final int MOVIE_TIME = 1200;
|
||||
private static final int FRAME_TIME = MOVIE_TIME / FRAMES_COUNT;
|
||||
|
||||
public static final Icon[] FRAMES = new Icon[FRAMES_COUNT];
|
||||
@@ -42,12 +39,13 @@ public class ExecutionNodeProgressAnimator implements Runnable, Disposable {
|
||||
private long myLastInvocationTime = -1;
|
||||
|
||||
private Alarm myAlarm;
|
||||
private Set<ExecutionNode> myNodes = new HashSet<>();
|
||||
private AbstractTreeBuilder myTreeBuilder;
|
||||
private List<ExecutionNode> myNodes = new SmartList<>();
|
||||
private BuildTreeConsoleView myTreeView;
|
||||
|
||||
public ExecutionNodeProgressAnimator(AbstractTreeBuilder builder) {
|
||||
Disposer.register(builder, this);
|
||||
init(builder);
|
||||
public ExecutionNodeProgressAnimator(BuildTreeConsoleView treeConsoleView) {
|
||||
Disposer.register(treeConsoleView, this);
|
||||
myAlarm = new Alarm();
|
||||
myTreeView = treeConsoleView;
|
||||
}
|
||||
|
||||
static {
|
||||
@@ -69,67 +67,56 @@ public class ExecutionNodeProgressAnimator implements Runnable, Disposable {
|
||||
return FRAMES[getCurrentFrameIndex()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes animator: creates alarm and sets tree builder
|
||||
*
|
||||
* @param treeBuilder tree builder
|
||||
*/
|
||||
protected void init(final AbstractTreeBuilder treeBuilder) {
|
||||
myAlarm = new Alarm();
|
||||
myTreeBuilder = treeBuilder;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
if (!myNodes.isEmpty()) {
|
||||
final long time = System.currentTimeMillis();
|
||||
// optimization:
|
||||
// we shouldn't repaint if this frame was painted in current interval
|
||||
if (time - myLastInvocationTime >= FRAME_TIME) {
|
||||
repaintSubTree();
|
||||
repaintTree();
|
||||
myLastInvocationTime = time;
|
||||
}
|
||||
}
|
||||
scheduleRepaint();
|
||||
}
|
||||
|
||||
public void addNode(@NotNull ExecutionNode node) {
|
||||
if (myNodes.add(node) && myNodes.size() == 1) {
|
||||
scheduleRepaint();
|
||||
}
|
||||
public void addNode(@Nullable final ExecutionNode currentNode) {
|
||||
myNodes.add(currentNode);
|
||||
}
|
||||
|
||||
public void removeNode(@NotNull ExecutionNode node) {
|
||||
if (myNodes.remove(node) && myNodes.isEmpty()) {
|
||||
repaintSubTree();
|
||||
if (myAlarm != null) {
|
||||
myAlarm.cancelAllRequests();
|
||||
}
|
||||
}
|
||||
public void startMovie() {
|
||||
scheduleRepaint();
|
||||
}
|
||||
|
||||
public void stopMovie() {
|
||||
repaintTree();
|
||||
myNodes.clear();
|
||||
cancelAlarm();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
myTreeBuilder = null;
|
||||
myTreeView = null;
|
||||
myNodes.clear();
|
||||
cancelAlarm();
|
||||
}
|
||||
|
||||
private void cancelAlarm() {
|
||||
if (myAlarm != null) {
|
||||
myAlarm.cancelAllRequests();
|
||||
myAlarm = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void repaintSubTree() {
|
||||
if (myTreeBuilder == null || myTreeBuilder.isDisposed()) return;
|
||||
private void repaintTree() {
|
||||
if (myTreeView == null || myTreeView.isDisposed()) return;
|
||||
|
||||
List<ExecutionNode> toRemove = ContainerUtil.newSmartList();
|
||||
for (ExecutionNode node : myNodes) {
|
||||
DefaultMutableTreeNode treeNode = myTreeBuilder.getUi().getNodeForElement(node, false);
|
||||
if (treeNode != null) {
|
||||
myTreeBuilder.queueUpdateFrom(node, false, false);
|
||||
}
|
||||
else {
|
||||
toRemove.add(node);
|
||||
for (Iterator<ExecutionNode> iterator = myNodes.iterator(); iterator.hasNext(); ) {
|
||||
ExecutionNode node = iterator.next();
|
||||
myTreeView.scheduleUpdate(node);
|
||||
if (!node.isRunning()) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
myNodes.removeAll(toRemove);
|
||||
}
|
||||
|
||||
private void scheduleRepaint() {
|
||||
@@ -137,8 +124,6 @@ public class ExecutionNodeProgressAnimator implements Runnable, Disposable {
|
||||
return;
|
||||
}
|
||||
myAlarm.cancelAllRequests();
|
||||
if (!myNodes.isEmpty()) {
|
||||
myAlarm.addRequest(this, FRAME_TIME);
|
||||
}
|
||||
myAlarm.addRequest(this, FRAME_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user