Skip to content

Commit

Permalink
SLI-585 Rework SonarLint tool window layout
Browse files Browse the repository at this point in the history
* Splitter layout will adapt depending on the docking position
* Default split value is 50%
* Removed useless borders on some panels
* Splitter now have a 1px border to "grab"
  • Loading branch information
henryju committed Jul 22, 2021
1 parent cbee22a commit 277979d
Show file tree
Hide file tree
Showing 8 changed files with 354 additions and 325 deletions.
46 changes: 18 additions & 28 deletions src/main/java/org/sonarlint/intellij/ui/AbstractIssuesPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
package org.sonarlint.intellij.ui;

import com.intellij.ide.OccurenceNavigator;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.actionSystem.ActionGroup;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.ActionToolbar;
Expand All @@ -29,12 +28,12 @@
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.SimpleToolWindowPanel;
import com.intellij.openapi.ui.Splitter;
import com.intellij.tools.SimpleActionGroup;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.components.JBTabbedPane;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.ui.tree.TreeUtil;

import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyAdapter;
Expand All @@ -43,14 +42,14 @@
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.swing.Box;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import org.sonarlint.intellij.editor.EditorDecorator;
import org.sonarlint.intellij.issue.LiveIssue;
import org.sonarlint.intellij.ui.nodes.AbstractNode;
Expand Down Expand Up @@ -89,36 +88,22 @@ public void refreshToolbar() {

private void createTabs() {
// Flows panel with tree
JScrollPane flowsPanel = ScrollPaneFactory.createScrollPane(flowsTree);
JScrollPane flowsPanel = ScrollPaneFactory.createScrollPane(flowsTree, true);
flowsPanel.getVerticalScrollBar().setUnitIncrement(10);

// Rule panel
rulePanel = new SonarLintRulePanel(project);
JScrollPane scrollableRulePanel = ScrollPaneFactory.createScrollPane(
rulePanel.getPanel(),
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
JScrollPane scrollableRulePanel = ScrollPaneFactory.createScrollPane(rulePanel.getPanel(), true);

scrollableRulePanel.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollableRulePanel.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollableRulePanel.getVerticalScrollBar().setUnitIncrement(10);

detailsTab = new JBTabbedPane();
detailsTab.insertTab("Rule", null, scrollableRulePanel, "Details about the rule", RULE_TAB_INDEX);
detailsTab.insertTab("Locations", null, flowsPanel, "All locations involved in the issue", LOCATIONS_TAB_INDEX);
}

protected JComponent createSplitter(JComponent c1, JComponent c2, String proportionProperty, boolean vertical, float defaultSplit) {
float savedProportion = PropertiesComponent.getInstance(project).getFloat(proportionProperty, defaultSplit);

final Splitter splitter = new Splitter(vertical);
splitter.setFirstComponent(c1);
splitter.setSecondComponent(c2);
splitter.setProportion(savedProportion);
splitter.setHonorComponentsMinimumSize(true);
splitter.addPropertyChangeListener(Splitter.PROP_PROPORTION,
evt -> PropertiesComponent.getInstance(project).setValue(proportionProperty, Float.toString(splitter.getProportion())));

return splitter;
}

protected void issueTreeSelectionChanged() {
IssueNode[] selectedNodes = tree.getSelectedNodes(IssueNode.class, null);
if (selectedNodes.length > 0) {
Expand Down Expand Up @@ -184,7 +169,8 @@ public void keyPressed(KeyEvent e) {
}
});
tree.addFocusListener(new FocusAdapter() {
@Override public void focusGained(FocusEvent e) {
@Override
public void focusGained(FocusEvent e) {
if (!e.isTemporary()) {
issueTreeSelectionChanged();
}
Expand All @@ -211,7 +197,8 @@ private OccurenceNavigator.OccurenceInfo occurrence(@Nullable IssueNode node) {
-1);
}

@Override public boolean hasNextOccurence() {
@Override
public boolean hasNextOccurence() {
// relies on the assumption that a TreeNodes will always be the last row in the table view of the tree
TreePath path = tree.getSelectionPath();
if (path == null) {
Expand All @@ -225,7 +212,8 @@ private OccurenceNavigator.OccurenceInfo occurrence(@Nullable IssueNode node) {
}
}

@Override public boolean hasPreviousOccurence() {
@Override
public boolean hasPreviousOccurence() {
TreePath path = tree.getSelectionPath();
if (path == null) {
return false;
Expand Down Expand Up @@ -259,18 +247,20 @@ public OccurenceNavigator.OccurenceInfo goPreviousOccurence() {
return occurrence(treeBuilder.getPreviousIssue((AbstractNode) path.getLastPathComponent()));
}

@Override public String getNextOccurenceActionName() {
@Override
public String getNextOccurenceActionName() {
return "Next Issue";
}

@Override public String getPreviousOccurenceActionName() {
@Override
public String getPreviousOccurenceActionName() {
return "Previous Issue";
}

public void setSelectedIssue(LiveIssue issue) {
DefaultMutableTreeNode issueNode = TreeUtil.findNode(((DefaultMutableTreeNode) tree.getModel().getRoot()),
(node) -> node instanceof IssueNode && ((IssueNode) node).issue().equals(issue));
if(issueNode == null) {
if (issueNode == null) {
return;
}
tree.setSelectionPath(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import org.sonarlint.intellij.messages.StatusListener;
import org.sonarlint.intellij.util.SonarLintActions;

import static org.sonarlint.intellij.ui.SonarLintToolWindowFactory.createSplitter;

public class SonarLintAnalysisResultsPanel extends AbstractIssuesPanel implements Disposable {
private static final String SPLIT_PROPORTION_PROPERTY = "SONARLINT_ANALYSIS_RESULTS_SPLIT_PROPORTION";

Expand All @@ -50,7 +52,7 @@ public SonarLintAnalysisResultsPanel(Project project) {
setToolbar(createActionGroup());

// Put everything together
super.setContent(createSplitter(issuesPanel, detailsTab, SPLIT_PROPORTION_PROPERTY, false, 0.65f));
super.setContent(createSplitter(project, this, this, issuesPanel, detailsTab, SPLIT_PROPORTION_PROPERTY, 0.5f));

// Subscribe to events
subscribeToEvents();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ public SonarLintHotspotDescriptionPanel(Project project) {
styleSheet.addRule("td.pad {padding: 0px 10px 0px 0px;}");

panel = new JPanel(new BorderLayout());
panel.setBorder(IdeBorderFactory.createBorder(SideBorder.LEFT));

JComponent titleComp = new JLabel("Select a hotspot to see more details", SwingConstants.CENTER);
panel.add(titleComp, BorderLayout.CENTER);
Expand Down
36 changes: 14 additions & 22 deletions src/main/java/org/sonarlint/intellij/ui/SonarLintHotspotsPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,22 @@
*/
package org.sonarlint.intellij.ui;

import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.SimpleToolWindowPanel;
import com.intellij.openapi.ui.Splitter;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.components.JBTabbedPane;

import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;

import org.sonarlint.intellij.issue.hotspot.LocalHotspot;
import org.sonarsource.sonarlint.core.serverapi.hotspot.ServerHotspot;

public class SonarLintHotspotsPanel extends SimpleToolWindowPanel {
import static org.sonarlint.intellij.ui.SonarLintToolWindowFactory.createSplitter;

public class SonarLintHotspotsPanel extends SimpleToolWindowPanel implements Disposable {
private static final String SPLIT_PROPORTION_PROPERTY = "SONARLINT_HOTSPOTS_SPLIT_PROPORTION";
private static final float DEFAULT_SPLIT_PROPORTION = 0.5f;

Expand All @@ -58,32 +61,17 @@ public SonarLintHotspotsPanel(Project project) {
hotspotDetailsTab.addTab("Details", null, scrollable(detailsPanel.getPanel()), "Details about the hotspot");
hotspotDetailsTab.setVisible(false);

super.setContent(createSplitter(hotspotsListPanel.getPanel(), hotspotDetailsTab, SPLIT_PROPORTION_PROPERTY, project));
super.setContent(createSplitter(project, this, this, hotspotsListPanel.getPanel(), hotspotDetailsTab, SPLIT_PROPORTION_PROPERTY, DEFAULT_SPLIT_PROPORTION));
}

private static JScrollPane scrollable(JComponent component) {
JScrollPane scrollableRulePanel = ScrollPaneFactory.createScrollPane(
component,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
JScrollPane scrollableRulePanel = ScrollPaneFactory.createScrollPane(component, true);
scrollableRulePanel.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollableRulePanel.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollableRulePanel.getVerticalScrollBar().setUnitIncrement(10);
return scrollableRulePanel;
}

protected JComponent createSplitter(JComponent c1, JComponent c2, String proportionProperty, Project project) {
float savedProportion = PropertiesComponent.getInstance(project).getFloat(proportionProperty, DEFAULT_SPLIT_PROPORTION);

final Splitter splitter = new Splitter(false);
splitter.setFirstComponent(c1);
splitter.setSecondComponent(c2);
splitter.setProportion(savedProportion);
splitter.setHonorComponentsMinimumSize(true);
splitter.addPropertyChangeListener(Splitter.PROP_PROPORTION,
evt -> PropertiesComponent.getInstance(project).setValue(proportionProperty, Float.toString(splitter.getProportion())));

return splitter;
}

public void setHotspot(LocalHotspot hotspot) {
hotspotDetailsTab.setVisible(true);
hotspotsListPanel.setHotspot(hotspot);
Expand All @@ -94,4 +82,8 @@ public void setHotspot(LocalHotspot hotspot) {
detailsPanel.setDetails(hotspot);
}

@Override
public void dispose() {
// Nothing to do
}
}
19 changes: 15 additions & 4 deletions src/main/java/org/sonarlint/intellij/ui/SonarLintIssuesPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@
*/
package org.sonarlint.intellij.ui;

import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.JBSplitter;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.util.ui.tree.TreeUtil;

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.util.ArrayList;
Expand All @@ -35,14 +37,18 @@
import java.util.List;
import javax.annotation.Nullable;
import javax.swing.JPanel;

import org.jetbrains.annotations.NonNls;
import org.sonarlint.intellij.issue.LiveIssue;
import org.sonarlint.intellij.util.SonarLintActions;
import org.sonarlint.intellij.util.SonarLintUtils;

public class SonarLintIssuesPanel extends AbstractIssuesPanel implements DataProvider {
import static org.sonarlint.intellij.ui.SonarLintToolWindowFactory.createSplitter;

public class SonarLintIssuesPanel extends AbstractIssuesPanel implements Disposable {
private static final String SPLIT_PROPORTION_PROPERTY = "SONARLINT_ISSUES_SPLIT_PROPORTION";
private final CurrentFileController scope;
private final JBSplitter splitter;

public SonarLintIssuesPanel(Project project, CurrentFileController scope) {
super(project);
Expand All @@ -54,11 +60,16 @@ public SonarLintIssuesPanel(Project project, CurrentFileController scope) {
issuesPanel.add(ScrollPaneFactory.createScrollPane(tree), BorderLayout.CENTER);
issuesPanel.add(new AutoTriggerStatusPanel(project).getPanel(), BorderLayout.SOUTH);

super.setContent(createSplitter(issuesPanel, detailsTab, SPLIT_PROPORTION_PROPERTY, false, 0.65f));

splitter = createSplitter(project, this, this, issuesPanel, detailsTab, SPLIT_PROPORTION_PROPERTY, 0.5f);
super.setContent(splitter);
subscribeToEvents();
}

@Override
public void dispose() {
// Nothing to do
}

private static Collection<AnAction> actions() {
SonarLintActions sonarLintActions = SonarLintActions.getInstance();
List<AnAction> list = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ public class SonarLintRulePanel {
public SonarLintRulePanel(Project project) {
this.project = project;
panel = new JPanel(new BorderLayout());
panel.setBorder(IdeBorderFactory.createBorder(SideBorder.LEFT));
setRuleKey(null);
show();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,22 @@
*/
package org.sonarlint.intellij.ui;

import com.intellij.openapi.Disposable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowAnchor;
import com.intellij.openapi.wm.ToolWindowFactory;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.openapi.wm.ToolWindowType;
import com.intellij.openapi.wm.ex.ToolWindowManagerListener;
import com.intellij.ui.JBSplitter;
import com.intellij.ui.OnePixelSplitter;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentManager;

import javax.swing.JComponent;

import org.jetbrains.annotations.NotNull;
import org.sonarlint.intellij.actions.SonarLintToolWindow;
import org.sonarlint.intellij.issue.IssueManager;
Expand Down Expand Up @@ -65,6 +74,40 @@ public void createToolWindowContent(Project project, final ToolWindow toolWindow
}
}

public static JBSplitter createSplitter(Project project, JComponent parentComponent, Disposable parentDisposable, JComponent c1, JComponent c2, String proportionProperty,
float defaultSplit) {
JBSplitter splitter = new OnePixelSplitter(splitVertically(project), proportionProperty, defaultSplit);
splitter.setFirstComponent(c1);
splitter.setSecondComponent(c2);
splitter.setHonorComponentsMinimumSize(true);

final ToolWindowManagerListener listener = new ToolWindowManagerListener() {
@Override
public void stateChanged(@NotNull ToolWindowManager toolWindowManager) {
splitter.setOrientation(splitVertically(project));
parentComponent.revalidate();
parentComponent.repaint();
}
};
project.getMessageBus().connect(parentDisposable).subscribe(ToolWindowManagerListener.TOPIC, listener);
Disposer.register(parentDisposable, () -> {
parentComponent.remove(splitter);
splitter.dispose();
});

return splitter;
}

public static boolean splitVertically(Project project) {
final ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow(SonarLintToolWindowFactory.TOOL_WINDOW_ID);
boolean splitVertically = false;
if (toolWindow != null) {
final ToolWindowAnchor anchor = toolWindow.getAnchor();
splitVertically = anchor == ToolWindowAnchor.LEFT || anchor == ToolWindowAnchor.RIGHT;
}
return splitVertically;
}

private static void addIssuesTab(Project project, @NotNull ContentManager contentManager) {
IssueManager issueManager = getService(project, IssueManager.class);
CurrentFileController scope = new CurrentFileController(project, issueManager);
Expand Down Expand Up @@ -95,16 +138,15 @@ private static void addAnalysisResultsTab(Project project, @NotNull ContentManag
private static void addTaintIssuesTab(Project project, @NotNull ContentManager contentManager) {
TaintVulnerabilitiesPanel vulnerabilitiesPanel = new TaintVulnerabilitiesPanel(project);
Content analysisResultsContent = contentManager.getFactory()
.createContent(
vulnerabilitiesPanel,
buildVulnerabilitiesTabName(0),
false);
.createContent(
vulnerabilitiesPanel,
buildVulnerabilitiesTabName(0),
false);
analysisResultsContent.setCloseable(false);
contentManager.addDataProvider(vulnerabilitiesPanel);
contentManager.addContent(analysisResultsContent);
}


private static void addLogTab(Project project, ToolWindow toolWindow) {
Content logContent = toolWindow.getContentManager().getFactory()
.createContent(
Expand Down
Loading

0 comments on commit 277979d

Please sign in to comment.