inspection tool window: replace quick fix preview with 2 separate components: 1st when loading 2nd when selected node is ready for fix or suppression

This commit is contained in:
Dmitry Batkovich
2016-04-21 11:08:29 +03:00
parent 8ef2d06622
commit 7e403ac3d7
3 changed files with 236 additions and 182 deletions
@@ -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(() -> {
@@ -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;
}
}
@@ -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<AnAction> 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:");
}
}