diff --git a/platform/lang-impl/src/com/intellij/build/BuildTreeConsoleView.java b/platform/lang-impl/src/com/intellij/build/BuildTreeConsoleView.java index abe12718187c..0058a90724b9 100644 --- a/platform/lang-impl/src/com/intellij/build/BuildTreeConsoleView.java +++ b/platform/lang-impl/src/com/intellij/build/BuildTreeConsoleView.java @@ -29,23 +29,18 @@ import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.Navigatable; import com.intellij.ui.*; -import com.intellij.ui.speedSearch.SpeedSearchUtil; import com.intellij.ui.tree.AsyncTreeModel; import com.intellij.ui.tree.StructureTreeModel; import com.intellij.ui.tree.TreeVisitor; -import com.intellij.ui.tree.treeTable.TreeTableModelWithColumns; import com.intellij.ui.treeStructure.SimpleNode; import com.intellij.ui.treeStructure.SimpleTreeStructure; -import com.intellij.ui.treeStructure.treetable.TreeColumnInfo; -import com.intellij.ui.treeStructure.treetable.TreeTable; -import com.intellij.ui.treeStructure.treetable.TreeTableModel; -import com.intellij.ui.treeStructure.treetable.TreeTableTree; +import com.intellij.ui.treeStructure.Tree; import com.intellij.util.EditSourceOnDoubleClickHandler; import com.intellij.util.EditSourceOnEnterKeyHandler; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.DateFormatUtil; -import com.intellij.util.ui.ColumnInfo; +import com.intellij.util.ui.GraphicsUtil; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; import org.jetbrains.annotations.NonNls; @@ -55,15 +50,11 @@ import org.jetbrains.annotations.TestOnly; import javax.swing.*; import javax.swing.border.CompoundBorder; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableColumn; +import javax.swing.plaf.TreeUI; import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; import java.awt.*; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; import java.io.File; import java.util.List; import java.util.*; @@ -73,6 +64,7 @@ import java.util.function.Predicate; import java.util.function.Supplier; import static com.intellij.build.BuildView.CONSOLE_VIEW_NAME; +import static com.intellij.ui.AnimatedIcon.ANIMATION_IN_RENDERER_ALLOWED; /** * @author Vladislav.Soroka @@ -87,19 +79,15 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon private final Project myProject; private final ConsoleViewHandler myConsoleViewHandler; - @NotNull - private final BuildViewSettingsProvider myViewSettingsProvider; - private final TableColumn myTimeColumn; private final String myWorkingDir; private final AtomicBoolean myDisposed = new AtomicBoolean(); private final AtomicBoolean myShownFirstError = new AtomicBoolean(); private final StructureTreeModel myTreeModel; - private final TreeTableTree myTree; + private final Tree myTree; private final ExecutionNode myRootNode; private final ExecutionNode myBuildProgressRootNode; @NotNull private final BuildViewGroupingSupport myGroupingSupport; private final ConcurrentLinkedDeque> myGroupingEvents = new ConcurrentLinkedDeque<>(); - private volatile int myTimeColumnWidth; @Nullable private volatile Predicate myExecutionTreeFilter; @@ -109,25 +97,6 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon @NotNull BuildViewSettingsProvider buildViewSettingsProvider) { myProject = project; myWorkingDir = FileUtil.toSystemIndependentName(buildDescriptor.getWorkingDir()); - final ColumnInfo[] COLUMNS = { - new TreeColumnInfo("name"), - new ColumnInfo("time elapsed") { - @Nullable - @Override - public Object valueOf(Object o) { - if (o instanceof DefaultMutableTreeNode) { - final Object userObject = ((DefaultMutableTreeNode)o).getUserObject(); - if (userObject instanceof ExecutionNode) { - String duration = ((ExecutionNode)userObject).getDuration(); - updateTimeColumnWidth("___" + duration, false); - return duration; - } - } - return null; - } - } - }; - myViewSettingsProvider = buildViewSettingsProvider; myGroupingSupport = new BuildViewGroupingSupport(this); myGroupingSupport.addPropertyChangeListener(e -> changeGrouping()); @@ -138,84 +107,11 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon SimpleTreeStructure treeStructure = new SimpleTreeStructure.Impl(myRootNode); myTreeModel = new StructureTreeModel<>(treeStructure); - final TreeTableModel model = new TreeTableModelWithColumns(new AsyncTreeModel(myTreeModel, this), COLUMNS); - - DefaultTableCellRenderer timeColumnCellRenderer = new DefaultTableCellRenderer() { - @Override - public Component getTableCellRendererComponent(JTable table, - Object value, - boolean isSelected, - boolean hasFocus, - int row, - int column) { - super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - setHorizontalAlignment(SwingConstants.RIGHT); - Color fg = isSelected ? UIUtil.getTreeSelectionForeground(hasFocus) : SimpleTextAttributes.GRAY_ATTRIBUTES.getFgColor(); - setForeground(fg); - return this; - } - }; - - TreeTable treeTable = new TreeTable(model) { - @Override - public TableCellRenderer getCellRenderer(int row, int column) { - if (column == 1) { - return timeColumnCellRenderer; - } - return super.getCellRenderer(row, column); - } - }; - treeTable.setRootVisible(false); - EditSourceOnDoubleClickHandler.install(treeTable); - EditSourceOnEnterKeyHandler.install(treeTable, null); - - myTree = treeTable.getTree(); - treeTable.addFocusListener(new FocusListener() { - @Override - public void focusGained(FocusEvent e) { - treeTable.setSelectionBackground(UIUtil.getTreeSelectionBackground(true)); - } - - @Override - public void focusLost(FocusEvent e) { - treeTable.setSelectionBackground(UIUtil.getTreeSelectionBackground(false)); - } - }); - final TreeCellRenderer treeCellRenderer = myTree.getCellRenderer(); - myTree.setCellRenderer((tree, value, selected, expanded, leaf, row, hasFocus) -> { - final Component rendererComponent = - treeCellRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); - if (rendererComponent instanceof SimpleColoredComponent) { - Color bg = UIUtil.getTreeBackground(selected, true); - Color fg = UIUtil.getTreeForeground(selected, true); - if (selected) { - for (SimpleColoredComponent.ColoredIterator it = ((SimpleColoredComponent)rendererComponent).iterator(); it.hasNext(); ) { - it.next(); - int offset = it.getOffset(); - int endOffset = it.getEndOffset(); - SimpleTextAttributes currentAttributes = it.getTextAttributes(); - SimpleTextAttributes newAttributes = - new SimpleTextAttributes(bg, fg, currentAttributes.getWaveColor(), currentAttributes.getStyle()); - it.split(endOffset - offset, newAttributes); - } - } - - SpeedSearchUtil.applySpeedSearchHighlighting(treeTable, (SimpleColoredComponent)rendererComponent, true, selected); - } - return rendererComponent; - }); - new TreeTableSpeedSearch(treeTable).setComparator(new SpeedSearchComparator(false)); - treeTable.setTableHeader(null); - - myTimeColumn = treeTable.getColumnModel().getColumn(1); - myTimeColumn.setResizable(false); - updateTimeColumnWidth(StringUtil.formatDurationApproximate(11111L), true); - - TreeUtil.installActions(myTree); + myTree = initTree(new AsyncTreeModel(myTreeModel, this)); JPanel myContentPanel = new JPanel(); myContentPanel.setLayout(new CardLayout()); - myContentPanel.add(ScrollPaneFactory.createScrollPane(treeTable, SideBorder.LEFT), TREE); + myContentPanel.add(ScrollPaneFactory.createScrollPane(myTree, SideBorder.LEFT), TREE); myPanel.setLayout(new BorderLayout()); ThreeComponentsSplitter myThreeComponentsSplitter = new ThreeComponentsSplitter() { @@ -244,6 +140,39 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon myPanel.add(myThreeComponentsSplitter, BorderLayout.CENTER); } + private static Tree initTree(@NotNull AsyncTreeModel model) { + Tree tree = new MyTree(model); + UIUtil.putClientProperty(tree, ANIMATION_IN_RENDERER_ALLOWED, true); + tree.setRootVisible(false); + EditSourceOnDoubleClickHandler.install(tree); + EditSourceOnEnterKeyHandler.install(tree, null); + new TreeSpeedSearch(tree).setComparator(new SpeedSearchComparator(false)); + TreeUtil.installActions(tree); + return tree; + } + + private void installContextMenu(@NotNull StartBuildEvent startBuildEvent) { + UIUtil.invokeLaterIfNeeded(() -> { + final DefaultActionGroup group = new DefaultActionGroup(); + final DefaultActionGroup rerunActionGroup = new DefaultActionGroup(); + AnAction[] restartActions = startBuildEvent.getRestartActions(); + for (AnAction anAction : restartActions) { + rerunActionGroup.add(anAction); + } + if (restartActions.length > 0) { + group.addAll(rerunActionGroup); + group.addSeparator(); + } + EditSourceAction edit = new EditSourceAction(); + ActionUtil.copyFrom(edit, "EditSource"); + group.add(edit); + group.addSeparator(); + group.add(new ShowExecutionErrorsOnlyAction(this)); + + PopupHandler.installPopupHandler(myTree, group, "BuildView"); + }); + } + @Override public void clear() { getRootElement().removeChildren(); @@ -367,13 +296,6 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon if (event instanceof FinishEvent) { currentNode.setEndTime(event.getEventTime()); currentNode.setResult(((FinishEvent)event).getResult()); - final String text = "__" + currentNode.getDuration(); - ApplicationManager.getApplication().invokeLater(() -> { - int timeColumnWidth = new JLabel(text, SwingConstants.RIGHT).getPreferredSize().width; - if (myTimeColumnWidth < timeColumnWidth) { - myTimeColumnWidth = timeColumnWidth; - } - }); } if (event instanceof FinishBuildEvent) { @@ -381,7 +303,6 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon String time = DateFormatUtil.formatDateTime(event.getEventTime()); aHint = aHint == null ? "at " + time : aHint + " at " + time; currentNode.setHint(aHint); - updateTimeColumnWidth(myTimeColumnWidth); if (myConsoleViewHandler.myExecutionNode == null) { ExecutionNode element = getBuildProgressRootNode(); ApplicationManager.getApplication().invokeLater(() -> myConsoleViewHandler.setNode(element)); @@ -390,27 +311,19 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon scheduleUpdate(currentNode); } - private void installContextMenu(@NotNull StartBuildEvent startBuildEvent) { - UIUtil.invokeLaterIfNeeded(() -> { - final DefaultActionGroup group = new DefaultActionGroup(); - final DefaultActionGroup rerunActionGroup = new DefaultActionGroup(); - AnAction[] restartActions = startBuildEvent.getRestartActions(); - for (AnAction anAction : restartActions) { - rerunActionGroup.add(anAction); - } - if (restartActions.length > 0) { - group.addAll(rerunActionGroup); - group.addSeparator(); - } - EditSourceAction edit = new EditSourceAction(); - ActionUtil.copyFrom(edit, "EditSource"); - group.add(edit); - group.addSeparator(); - group.add(new ShowExecutionErrorsOnlyAction(this)); - - TreeTable treeTable = myTree.getTreeTable(); - PopupHandler.installPopupHandler(treeTable, group, "BuildView"); - }); + protected void expand(Tree tree) { + TreeUtil.expand(tree, + path -> { + ExecutionNode node = TreeUtil.getLastUserObject(ExecutionNode.class, path); + if (node != null && node.isAutoExpandNode() && node.getChildCount() > 0) { + return TreeVisitor.Action.CONTINUE; + } + else { + return TreeVisitor.Action.SKIP_CHILDREN; + } + }, + path -> { + }); } @Override @@ -495,19 +408,6 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon myTreeModel.getInvoker().runOrInvokeLater(() -> onEventInternal(event)); } - protected void expand(TreeTableTree tree) { - TreeUtil.expand(tree, - path -> { - ExecutionNode node = TreeUtil.getLastUserObject(ExecutionNode.class, path); - if (node != null && node.isAutoExpandNode() && node.getChildCount() > 0) { - return TreeVisitor.Action.CONTINUE; - } else { - return TreeVisitor.Action.SKIP_CHILDREN; - } - }, - path -> {}); - } - void scheduleUpdate(ExecutionNode executionNode) { SimpleNode node = executionNode.getParent() == null ? executionNode : executionNode.getParent(); myTreeModel.invalidate(node, true).onProcessed(p -> expand(myTree)); @@ -598,17 +498,6 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon }); } - private void updateTimeColumnWidth(String text, boolean force) { - int timeColumnWidth = new JLabel(text, SwingConstants.RIGHT).getPreferredSize().width; - if (myTimeColumnWidth > timeColumnWidth) { - timeColumnWidth = myTimeColumnWidth; - } - - if (force || myTimeColumn.getMaxWidth() < timeColumnWidth || myTimeColumn.getWidth() < timeColumnWidth) { - updateTimeColumnWidth(timeColumnWidth); - } - } - private void changeGrouping() { for (Pair pair : myGroupingEvents) { BuildEvent event = pair.first; @@ -650,12 +539,6 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon } } - private void updateTimeColumnWidth(int width) { - myTimeColumn.setPreferredWidth(width); - myTimeColumn.setMinWidth(width); - myTimeColumn.setMaxWidth(width); - } - @Nullable @Override public Object getData(@NotNull String dataId) { @@ -747,7 +630,7 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon private ExecutionNode myExecutionNode; ConsoleViewHandler(Project project, - TreeTableTree tree, + @NotNull Tree tree, @NotNull ExecutionNode buildProgressRootNode, ThreeComponentsSplitter threeComponentsSplitter, @Nullable ExecutionConsole executionConsole, @@ -873,4 +756,72 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon getTaskOutputView().clear(); } } + + private static class MyTree extends Tree { + private MyTree(TreeModel treemodel) { + super(treemodel); + } + + @Override + public void setUI(final TreeUI ui) { + super.setUI(ui); + final int fontHeight = getFontMetrics(getFont()).getHeight(); + final int iconHeight = AllIcons.RunConfigurations.TestPassed.getIconHeight(); + setRowHeight(Math.max(fontHeight, iconHeight) + 2); + setLargeModel(true); + } + + public boolean isExpandableHandlerVisibleForCurrentRow(int row) { + final ExpandableItemsHandler handler = getExpandableItemsHandler(); + final Collection items = handler.getExpandedItems(); + return items.size() == 1 && row == items.iterator().next(); + } + + @Override + public void paint(Graphics g) { + super.paint(g); + Rectangle visibleRect = getVisibleRect(); + Rectangle clip = g.getClipBounds(); + final int visibleRowCount = TreeUtil.getVisibleRowCountForFixedRowHeight(this); + final int firstRow = getClosestRowForLocation(0, visibleRect.y); + for (int row = firstRow; row < Math.min(firstRow + visibleRowCount + 1, getRowCount()); row++) { + if (isExpandableHandlerVisibleForCurrentRow(row)) continue; + if (row == -1) continue; + Object node = getPathForRow(row).getLastPathComponent(); + if (!(node instanceof DefaultMutableTreeNode)) continue; + Object data = ((DefaultMutableTreeNode)node).getUserObject(); + if (!(data instanceof ExecutionNode)) continue; + final String duration = ((ExecutionNode)data).getDuration(); + if (duration == null) continue; + + Rectangle rowBounds = getRowBounds(row); + rowBounds.x = 0; + rowBounds.width = Integer.MAX_VALUE; + + if (rowBounds.intersects(clip)) { + Rectangle fullRowRect = new Rectangle(visibleRect.x, rowBounds.y, visibleRect.width, rowBounds.height); + paintRowData(this, duration, fullRowRect, (Graphics2D)g, isRowSelected(row), hasFocus()); + } + } + } + + private static void paintRowData(Tree tree, String duration, Rectangle bounds, Graphics2D g, boolean isSelected, boolean hasFocus) { + g.setFont(tree.getFont().deriveFont(Font.PLAIN, UIUtil.getFontSize(UIUtil.FontSize.SMALL))); + int totalWidth = tree.getFontMetrics(g.getFont()).stringWidth(duration) + 2; + int x = bounds.x + bounds.width - totalWidth; + g.setColor(isSelected ? UIUtil.getTreeSelectionBackground(hasFocus) : UIUtil.getTreeBackground()); + int leftOffset = 5; + g.fillRect(x - leftOffset, bounds.y, totalWidth + leftOffset, bounds.height); + g.translate(0, bounds.y - 1); + if (isSelected) { + g.setColor(UIUtil.getTreeSelectionForeground(hasFocus)); + } + else { + g.setColor(new JBColor(0x808080, 0x808080)); + } + g.drawString(duration, x, SimpleColoredComponent.getTextBaseLine(tree.getFontMetrics(tree.getFont()), bounds.height) + 1); + g.translate(0, -bounds.y + 1); + GraphicsUtil.setupAAPainting(g).restore(); + } + } } diff --git a/platform/lang-impl/src/com/intellij/build/ExecutionNode.java b/platform/lang-impl/src/com/intellij/build/ExecutionNode.java index e0574537fb78..e0b2abc50d6a 100644 --- a/platform/lang-impl/src/com/intellij/build/ExecutionNode.java +++ b/platform/lang-impl/src/com/intellij/build/ExecutionNode.java @@ -28,12 +28,10 @@ import com.intellij.ui.treeStructure.CachingSimpleNode; import com.intellij.ui.treeStructure.SimpleNode; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.awt.*; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -57,16 +55,7 @@ public class ExecutionNode extends CachingSimpleNode { private static final Icon NODE_ICON_STATISTICS = ICON_16; private static final Icon NODE_ICON_SIMPLE = ICON_16; private static final Icon NODE_ICON_DEFAULT = ICON_16; - private static final Icon NODE_ICON_RUNNING = new AnimatedIcon.FS() { - @Override - protected Component getRendererOwner(Component component) { - if (component instanceof JTree && !component.isShowing()) { - CellRendererPane pane = UIUtil.getParentOfType(CellRendererPane.class, component); - if (pane != null) return pane.getParent(); - } - return null; - } - }; + private static final Icon NODE_ICON_RUNNING = new AnimatedIcon.FS(); private final Collection myChildrenList = new ConcurrentLinkedDeque<>(); //ContainerUtil.newSmartList(); private final AtomicInteger myErrors = new AtomicInteger();