From 7e403ac3d7415d00b60bb79349635b922423b937 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Thu, 21 Apr 2016 11:07:40 +0300 Subject: [PATCH] inspection tool window: replace quick fix preview with 2 separate components: 1st when loading 2nd when selected node is ready for fix or suppression --- .../ui/InspectionResultsView.java | 17 +- .../ui/QuickFixPreviewDecorator.java | 177 -------------- .../ui/QuickFixPreviewPanelFactory.java | 224 ++++++++++++++++++ 3 files changed, 236 insertions(+), 182 deletions(-) delete mode 100644 platform/lang-impl/src/com/intellij/codeInspection/ui/QuickFixPreviewDecorator.java create mode 100644 platform/lang-impl/src/com/intellij/codeInspection/ui/QuickFixPreviewPanelFactory.java diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java index ffebc21a6150..63a5630637d5 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java @@ -468,9 +468,16 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren } editorPanel.add(previewPanel, BorderLayout.CENTER); if (problemCount > 0) { - final QuickFixPreviewDecorator fixToolbar = new QuickFixPreviewDecorator(previewEditor, this); - myLoadingProgressPreview = fixToolbar; - editorPanel.add(fixToolbar, BorderLayout.NORTH); + final JComponent fixToolbar = QuickFixPreviewPanelFactory.create(previewEditor, this); + if (fixToolbar != null) { + if (fixToolbar instanceof InspectionTreeLoadingProgressAware) { + myLoadingProgressPreview = (InspectionTreeLoadingProgressAware)fixToolbar; + } + if (previewEditor != null) { + previewPanel.setBorder(IdeBorderFactory.createBorder(SideBorder.TOP)); + } + editorPanel.add(fixToolbar, BorderLayout.NORTH); + } } mySplitter.setSecondComponent(editorPanel); } @@ -516,10 +523,10 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren settings.setAdditionalLinesCount(0); settings.setLeadingWhitespaceShown(true); myPreviewEditor.getColorsScheme().setColor(EditorColors.GUTTER_BACKGROUND, myPreviewEditor.getColorsScheme().getDefaultBackground()); - myPreviewEditor.getScrollPane().setBorder(IdeBorderFactory.createBorder(SideBorder.TOP)); + myPreviewEditor.getScrollPane().setBorder(IdeBorderFactory.createEmptyBorder()); } myPreviewEditor.getSettings().setFoldingOutlineShown(problemCount != 1); - + myPreviewEditor.getComponent().setBorder(IdeBorderFactory.createEmptyBorder()); if (problemCount == 1) { final PsiElement finalSelectedElement = selectedElement; ApplicationManager.getApplication().invokeLater(() -> { diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/QuickFixPreviewDecorator.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/QuickFixPreviewDecorator.java deleted file mode 100644 index cd932d735742..000000000000 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/QuickFixPreviewDecorator.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright 2000-2016 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.codeInspection.ui; - -import com.intellij.codeInspection.CommonProblemDescriptor; -import com.intellij.codeInspection.ex.InspectionToolWrapper; -import com.intellij.codeInspection.ex.QuickFixAction; -import com.intellij.codeInspection.ui.actions.SuppressActionWrapper; -import com.intellij.icons.AllIcons; -import com.intellij.ide.DataManager; -import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.actionSystem.ex.ComboBoxAction; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.openapi.util.Disposer; -import com.intellij.ui.IdeBorderFactory; -import com.intellij.ui.SimpleColoredComponent; -import com.intellij.util.ui.AsyncProcessIcon; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.awt.*; - -/** - * @author Dmitry Batkovich - */ -public class QuickFixPreviewDecorator extends JPanel implements InspectionTreeLoadingProgressAware { - private static final Logger LOG = Logger.getInstance(QuickFixPreviewDecorator.class); - private static final int MAX_FIX_COUNT = 3; - @NotNull private final InspectionResultsView myView; - private final InspectionToolWrapper myWrapper; - private final ProblemPreviewEditorPresentation myFoldings; - - private SimpleColoredComponent myWaitingLabel; - - public QuickFixPreviewDecorator(@Nullable EditorEx editor, - @NotNull InspectionResultsView view) { - myFoldings = editor == null ? null : new ProblemPreviewEditorPresentation(editor, view.getProject()); - myView = view; - myWrapper = view.getTree().getSelectedToolWrapper(); - LOG.assertTrue(myWrapper != null); - - setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); - - if (view.isUpdating() && !view.getTree().areDescriptorNodesSelected()) { - setBorder(IdeBorderFactory.createEmptyBorder(16, 9, 13, 0)); - AsyncProcessIcon waitingIcon = new AsyncProcessIcon("Inspection preview panel updating..."); - Disposer.register(this, waitingIcon); - myWaitingLabel = getLabel(1); - add(myWaitingLabel); - add(waitingIcon); - } - else { - CommonProblemDescriptor[] descriptors = myView.getTree().getSelectedDescriptors(); - setBorder(IdeBorderFactory.createEmptyBorder(2, 8, 0, 0)); - QuickFixAction[] fixes = view.getProvider().getQuickFixes(myWrapper, view.getTree()); - fillPanel(fixes, descriptors); - } - } - - @Override - public void treeLoaded() { - if (myWaitingLabel != null) { - removeAll(); - setBorder(IdeBorderFactory.createEmptyBorder(2, 8, 0, 0)); - final InspectionTree tree = myView.getTree(); - QuickFixAction[] fixes = myView.getProvider().getQuickFixes(myWrapper, tree); - CommonProblemDescriptor[] descriptors = tree.getSelectedDescriptors(); - fillPanel(fixes, descriptors); - revalidate(); - repaint(); - } - } - - @Override - public void updateLoadingProgress() { - if (myWaitingLabel != null) { - myWaitingLabel.clear(); - final InspectionTree tree = myView.getTree(); - appendTextToLabel(myWaitingLabel, tree.getSelectedProblemCount()); - } - } - - private void fillPanel(@Nullable QuickFixAction[] fixes, - CommonProblemDescriptor[] descriptors) { - if (myFoldings != null) myFoldings.appendFoldings(descriptors); - boolean hasFixes = fixes != null && fixes.length != 0; - int problemCount = descriptors.length; - boolean multipleDescriptors = problemCount > 1; - setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS)); - if (multipleDescriptors) { - add(getLabel(problemCount)); - } - - final DefaultActionGroup actions = new DefaultActionGroup(); - if (hasFixes) { - actions.addAll(createFixActions(fixes, multipleDescriptors)); - } - actions.add(createSuppressionCombo(myView)); - final ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, actions, true); - final JComponent component = toolbar.getComponent(); - toolbar.setTargetComponent(this); - add(component); - } - - @NotNull - private static SimpleColoredComponent getLabel(int problemsCount) { - SimpleColoredComponent label = new SimpleColoredComponent(); - appendTextToLabel(label, problemsCount); - label.setBorder(IdeBorderFactory.createEmptyBorder(0, 0, 0, 2)); - return label; - } - - private static void appendTextToLabel(SimpleColoredComponent label, - int problemsCount) { - label.append(problemsCount + " problems:"); - } - - private static AnAction createSuppressionCombo(InspectionResultsView view) { - final AnAction[] suppressors = new SuppressActionWrapper().getChildren(AnActionEvent.createFromDataContext(ActionPlaces.CODE_INSPECTION, null, DataManager.getInstance().getDataContext(view))); - final ComboBoxAction action = new ComboBoxAction() { - { - getTemplatePresentation().setText("Suppress"); - getTemplatePresentation().setEnabledAndVisible(suppressors.length != 0); - } - - @NotNull - @Override - protected DefaultActionGroup createPopupActionGroup(JComponent button) { - DefaultActionGroup group = new DefaultCompactActionGroup(); - group.addAll(suppressors); - return group; - } - }; - action.setSmallVariant(false); - return action; - } - - @NotNull - private static AnAction[] createFixActions(QuickFixAction[] fixes, boolean multipleDescriptors) { - if (fixes.length > MAX_FIX_COUNT) { - final ComboBoxAction fixComboBox = new ComboBoxAction() { - { - getTemplatePresentation().setText("Apply quick fixes" + (multipleDescriptors ? " to all the problems" : "")); - getTemplatePresentation().setIcon(AllIcons.Actions.CreateFromUsage); - setSmallVariant(false); - } - - @NotNull - @Override - protected DefaultActionGroup createPopupActionGroup(JComponent button) { - final DefaultActionGroup actionGroup = new DefaultActionGroup(); - for (QuickFixAction fix : fixes) { - actionGroup.add(fix); - } - return actionGroup; - } - }; - return new AnAction[] {fixComboBox}; - } - return fixes; - } -} diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/QuickFixPreviewPanelFactory.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/QuickFixPreviewPanelFactory.java new file mode 100644 index 000000000000..a3ddfd000373 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/QuickFixPreviewPanelFactory.java @@ -0,0 +1,224 @@ +/* + * Copyright 2000-2016 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.codeInspection.ui; + +import com.intellij.codeInspection.CommonProblemDescriptor; +import com.intellij.codeInspection.ex.InspectionToolWrapper; +import com.intellij.codeInspection.ex.QuickFixAction; +import com.intellij.codeInspection.ui.actions.SuppressActionWrapper; +import com.intellij.icons.AllIcons; +import com.intellij.ide.DataManager; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.actionSystem.ex.ComboBoxAction; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.util.Disposer; +import com.intellij.ui.IdeBorderFactory; +import com.intellij.ui.SimpleColoredComponent; +import com.intellij.util.ui.AsyncProcessIcon; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.util.Arrays; +import java.util.stream.Stream; + +/** + * @author Dmitry Batkovich + */ +public class QuickFixPreviewPanelFactory { + private static final Logger LOG = Logger.getInstance(QuickFixPreviewPanelFactory.class); + private static final int MAX_FIX_COUNT = 3; + + @Nullable + public static JComponent create(@Nullable EditorEx editor, + @NotNull InspectionResultsView view) { + if (view.isUpdating() && !view.getTree().areDescriptorNodesSelected()) { + return new LoadingInProgressPreview(view); + } + else { + final QuickFixReadyPanel panel = new QuickFixReadyPanel(view, editor); + return panel.isEmpty() ? null : panel; + } + } + + private static class QuickFixReadyPanel extends JPanel { + @NotNull private final InspectionResultsView myView; + private final InspectionToolWrapper myWrapper; + private final ProblemPreviewEditorPresentation myFoldings; + private final boolean myEmpty; + + public QuickFixReadyPanel(@NotNull InspectionResultsView view, EditorEx editor) { + myView = view; + myWrapper = view.getTree().getSelectedToolWrapper(); + LOG.assertTrue(myWrapper != null); + myFoldings = editor == null ? null : new ProblemPreviewEditorPresentation(editor, view.getProject()); + CommonProblemDescriptor[] descriptors = myView.getTree().getSelectedDescriptors(); + QuickFixAction[] fixes = view.getProvider().getQuickFixes(myWrapper, view.getTree()); + myEmpty = fillPanel(fixes, descriptors); + } + + public boolean isEmpty() { + return myEmpty; + } + + private boolean fillPanel(@Nullable QuickFixAction[] fixes, + CommonProblemDescriptor[] descriptors) { + if (myFoldings != null) myFoldings.appendFoldings(descriptors); + boolean hasFixes = fixes != null && fixes.length != 0; + int problemCount = descriptors.length; + boolean multipleDescriptors = problemCount > 1; + setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS)); + boolean hasComponents = false; + + if (multipleDescriptors) { + add(getLabel(problemCount)); + hasComponents = true; + } + + final DefaultActionGroup actions = new DefaultActionGroup(); + if (hasFixes) { + actions.addAll(createFixActions(fixes, multipleDescriptors)); + } + final AnAction suppressionCombo = createSuppressionCombo(myView); + if (suppressionCombo != null) { + actions.add(suppressionCombo); + } + if (actions.getChildrenCount() != 0) { + final ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, actions, true); + final JComponent component = toolbar.getComponent(); + toolbar.setTargetComponent(this); + add(component); + hasComponents = true; + } + + if (hasComponents) { + setBorder(IdeBorderFactory.createEmptyBorder(2, 8, 0, 0)); + } + return !hasComponents; + } + + @Nullable + private static AnAction createSuppressionCombo(InspectionResultsView view) { + final AnActionEvent + event = AnActionEvent.createFromDataContext(ActionPlaces.CODE_INSPECTION, null, DataManager.getInstance().getDataContext(view)); + final AnAction[] suppressors = new SuppressActionWrapper().getChildren(event); + final Stream suppressActionStream = Arrays.stream(suppressors).filter(s -> { + s.update(event); + return event.getPresentation().isEnabled(); + }); + if (!suppressActionStream.findFirst().isPresent()) { + return null; + } + final ComboBoxAction action = new ComboBoxAction() { + { + getTemplatePresentation().setText("Suppress"); + getTemplatePresentation().setEnabledAndVisible(suppressors.length != 0); + } + + @NotNull + @Override + protected DefaultActionGroup createPopupActionGroup(JComponent button) { + DefaultActionGroup group = new DefaultCompactActionGroup(); + group.addAll(suppressors); + return group; + } + }; + action.setSmallVariant(false); + return action; + } + + @NotNull + private static AnAction[] createFixActions(QuickFixAction[] fixes, boolean multipleDescriptors) { + if (fixes.length > MAX_FIX_COUNT) { + final ComboBoxAction fixComboBox = new ComboBoxAction() { + { + getTemplatePresentation().setText("Apply quick fixes" + (multipleDescriptors ? " to all the problems" : "")); + getTemplatePresentation().setIcon(AllIcons.Actions.CreateFromUsage); + setSmallVariant(false); + } + + @NotNull + @Override + protected DefaultActionGroup createPopupActionGroup(JComponent button) { + final DefaultActionGroup actionGroup = new DefaultActionGroup(); + for (QuickFixAction fix : fixes) { + actionGroup.add(fix); + } + return actionGroup; + } + }; + return new AnAction[] {fixComboBox}; + } + return fixes; + } + + } + + + private static class LoadingInProgressPreview extends JPanel implements InspectionTreeLoadingProgressAware { + private final InspectionResultsView myView; + private SimpleColoredComponent myWaitingLabel; + + private LoadingInProgressPreview(InspectionResultsView view) { + myView = view; + setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); + setBorder(IdeBorderFactory.createEmptyBorder(16, 9, 13, 0)); + AsyncProcessIcon waitingIcon = new AsyncProcessIcon("Inspection preview panel updating..."); + Disposer.register(this, waitingIcon); + myWaitingLabel = getLabel(1); + add(myWaitingLabel); + add(waitingIcon); + } + + @Override + public void updateLoadingProgress() { + if (myWaitingLabel != null) { + myWaitingLabel.clear(); + final InspectionTree tree = myView.getTree(); + appendTextToLabel(myWaitingLabel, tree.getSelectedProblemCount()); + } + } + + @Override + public void treeLoaded() { + ApplicationManager.getApplication().invokeLater(() -> { + if (myView.isValid()) { + myView.syncRightPanel(); + } + }); + } + + @Override + public void dispose() { + } + } + + @NotNull + private static SimpleColoredComponent getLabel(int problemsCount) { + SimpleColoredComponent label = new SimpleColoredComponent(); + appendTextToLabel(label, problemsCount); + label.setBorder(IdeBorderFactory.createEmptyBorder(0, 0, 0, 2)); + return label; + } + + private static void appendTextToLabel(SimpleColoredComponent label, + int problemsCount) { + label.append(problemsCount + " problems:"); + } +}