Skip to content

Commit a955aaa

Browse files
committed
GUI: minor improvements
- Option to sort outline - Option to restrict outline to 'major' elements - Option to browse selected folder in terminal - Extend tab customizations to all tabbed panes - Ensure 'non-executable-syntax' choice reflects active pane - Ensure menu bar separators are displayed on MacOS - Ensure EditorPane has focus when calling one of its actions - Fix contextual menu not being displayed on Outline panel - Fix bookmarks prompt being displayed twice - Fix vertical alignment of search field - Fix inconsistencies with scaled fold icons - Code cleanup: Aggregate GUI-related code in common class
1 parent ba6252b commit a955aaa

File tree

5 files changed

+266
-144
lines changed

5 files changed

+266
-144
lines changed

src/main/java/org/scijava/ui/swing/script/EditorPane.java

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import javax.swing.JRadioButtonMenuItem;
6262
import javax.swing.JScrollPane;
6363
import javax.swing.JViewport;
64+
import javax.swing.MenuElement;
6465
import javax.swing.ToolTipManager;
6566
import javax.swing.UIManager;
6667
import javax.swing.event.DocumentEvent;
@@ -184,7 +185,7 @@ public void hyperlinkUpdate(final HyperlinkEvent hle) {
184185
wordMovement("Prev-Word-Select-Action", -1, true));
185186

186187
noneLangSyntaxMenu = geSyntaxForNoneLang();
187-
installCostumPopupMenu();
188+
installCustomPopupMenu();
188189

189190
ToolTipManager.sharedInstance().registerComponent(this);
190191
addMouseListener(new MouseAdapter() {
@@ -236,30 +237,30 @@ public void addNotify() {
236237
if (isVisible()) super.addNotify();
237238
}
238239

239-
private void installCostumPopupMenu() {
240+
private void installCustomPopupMenu() {
240241
// We are overriding the entire menu so that we can include our shortcuts
241242
// in the menu items. These commands are not listed on the menubar, so
242243
// this is the only access point in the GUI for these
243244
// see #createPopupMenu(); #appendFoldingMenu();
244245
final JPopupMenu popup = new JPopupMenu();
245-
TextEditor.addPopupMenuSeparator(popup, "Code Editing:");
246+
TextEditor.GuiUtils.addPopupMenuSeparator(popup, "Code Editing:");
246247
popup.add(getMenuItem("Copy", EditorPaneActions.copyAction, false));
247248
popup.add(getMenuItem("Cut", EditorPaneActions.cutAction, true));
248249
popup.add(getMenuItem("Paste", EditorPaneActions.pasteAction, true));
249250
popup.add(getMenuItem("Delete Line", EditorPaneActions.rtaDeleteLineAction, true));
250251
popup.add(getMenuItem("Delete Rest of Line", EditorPaneActions.rtaDeleteRestOfLineAction, true));
251-
TextEditor.addPopupMenuSeparator(popup, "Code Selection:");
252+
TextEditor.GuiUtils.addPopupMenuSeparator(popup, "Code Selection:");
252253
popup.add(getMenuItem("Select All", EditorPaneActions.selectAllAction, false));
253254
popup.add(getMenuItem("Select Line", EditorPaneActions.selectLineAction, false));
254255
popup.add(getMenuItem("Select Paragraph", EditorPaneActions.selectParagraphAction, false));
255-
TextEditor.addPopupMenuSeparator(popup, "Code Folding:");
256+
TextEditor.GuiUtils.addPopupMenuSeparator(popup, "Code Folding:");
256257
popup.add(getMenuItem("Collapse Fold", EditorPaneActions.rstaCollapseFoldAction, false));
257258
popup.add(getMenuItem("Expand Fold", EditorPaneActions.rstaExpandFoldAction, false));
258259
popup.add(getMenuItem("Toggle Current Fold", EditorPaneActions.rstaToggleCurrentFoldAction, false));
259260
popup.add(getMenuItem("Collapse All Folds", EditorPaneActions.rstaCollapseAllFoldsAction, false));
260261
popup.add(getMenuItem("Expand All Folds", EditorPaneActions.rstaExpandAllFoldsAction, false));
261262
popup.add(getMenuItem("Collapse All Comments", EditorPaneActions.rstaCollapseAllCommentFoldsAction, false));
262-
TextEditor.addPopupMenuSeparator(popup, "Code Formatting:");
263+
TextEditor.GuiUtils.addPopupMenuSeparator(popup, "Code Formatting:");
263264
popup.add(getMenuItem("Indent Right", EditorPaneActions.epaIncreaseIndentAction, true));
264265
popup.add(getMenuItem("Indent Left", EditorPaneActions.rstaDecreaseIndentAction, true));
265266
popup.add(getMenuItem("Move Up", EditorPaneActions.rtaLineUpAction, true));
@@ -274,10 +275,10 @@ private void installCostumPopupMenu() {
274275
menu.add(getMenuItem("Lower Case ('_' Sep.)", EditorPaneActions.epaLowerCaseUndAction, true));
275276
menu.add(getMenuItem("Title Case", EditorPaneActions.epaTitleCaseAction, true));
276277
menu.add(getMenuItem("Upper Case", EditorPaneActions.rtaUpperSelectionCaseAction, true));
277-
TextEditor.addPopupMenuSeparator(popup, "Ocurrences:");
278+
TextEditor.GuiUtils.addPopupMenuSeparator(popup, "Ocurrences:");
278279
popup.add(getMenuItem("Next Occurrence", EditorPaneActions.rtaNextOccurrenceAction, false));
279280
popup.add(getMenuItem("Previous Occurrence", EditorPaneActions.rtaPrevOccurrenceAction, false));
280-
TextEditor.addPopupMenuSeparator(popup, "Utilities:");
281+
TextEditor.GuiUtils.addPopupMenuSeparator(popup, "Utilities:");
281282
popup.add(new OpenLinkUnderCursor().getMenuItem());
282283
popup.add(new SearchWebOnSelectedText().getMenuItem());
283284
popup.add(noneLangSyntaxMenu);
@@ -323,6 +324,7 @@ private JMenu geSyntaxForNoneLang() {
323324
private JMenuItem getSyntaxItem(final ButtonGroup bg, final String label, final String syntaxId) {
324325
final JRadioButtonMenuItem item = new JRadioButtonMenuItem(label);
325326
bg.add(item);
327+
item.setActionCommand(syntaxId);
326328
item.addActionListener(e -> {
327329
if (getCurrentLanguage() == null) {
328330
setSyntaxEditingStyle(syntaxId);
@@ -335,6 +337,30 @@ private JMenuItem getSyntaxItem(final ButtonGroup bg, final String label, final
335337
return item;
336338
}
337339

340+
private void updateNoneLangSyntaxMenu(final ScriptLanguage language) {
341+
noneLangSyntaxMenu.setEnabled(language == null);
342+
if (language == null) {
343+
JRadioButtonMenuItem defaultChoice = null;
344+
try {
345+
for (final MenuElement me : noneLangSyntaxMenu.getSubElements()) {
346+
if (me instanceof JRadioButtonMenuItem) {
347+
final JRadioButtonMenuItem rb = ((JRadioButtonMenuItem) me);
348+
final String choice = rb.getActionCommand();
349+
if (getSyntaxEditingStyle().equals(choice)) {
350+
rb.setSelected(true);
351+
return;
352+
}
353+
if (SYNTAX_STYLE_NONE.equals(choice))
354+
defaultChoice = rb;
355+
}
356+
}
357+
defaultChoice.setSelected(true);
358+
} catch (final Exception ignored) {
359+
// do nothing
360+
}
361+
}
362+
}
363+
338364
@Override
339365
public void setTabSize(final int width) {
340366
if (getTabSize() != width) super.setTabSize(width);
@@ -740,7 +766,7 @@ protected void setLanguage(final ScriptLanguage language,
740766
setText(header += getText());
741767
}
742768

743-
noneLangSyntaxMenu.setEnabled(language == null);
769+
updateNoneLangSyntaxMenu(language);
744770
if ("None".equals(languageName) ) {
745771
supportStatus = "Active language: None";
746772
return; // no need to update console any further

src/main/java/org/scijava/ui/swing/script/FileSystemTreePanel.java

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ class FileSystemTreePanel extends JPanel {
9999
bc.gridy = 0;
100100
bc.weightx = 0;
101101
bc.weighty = 0;
102-
bc.anchor = GridBagConstraints.NORTHWEST;
103-
bc.fill = GridBagConstraints.NONE;
102+
bc.anchor = GridBagConstraints.CENTER;
103+
bc.fill = GridBagConstraints.HORIZONTAL;
104104
add(addDirectoryButton(), bc);
105105
bc.gridx = 1;
106106
add(removeDirectoryButton(), bc);
@@ -322,13 +322,13 @@ private boolean allTreeNodesCollapsed() {
322322
private void addContextualMenuToTree() {
323323
final JPopupMenu popup = new JPopupMenu();
324324
JMenuItem jmi = new JMenuItem("Collapse All");
325-
jmi.addActionListener(e -> collapseAllNodes());
325+
jmi.addActionListener(e -> TextEditor.GuiUtils.collapseAllTreeNodes(tree));
326326
popup.add(jmi);
327327
jmi = new JMenuItem("Expand Folders");
328328
jmi.addActionListener(e -> expandImmediateNodes());
329329
popup.add(jmi);
330330
popup.addSeparator();
331-
jmi = new JMenuItem("Show in System Explorer");
331+
jmi = new JMenuItem("Open in System Explorer");
332332
jmi.addActionListener(e -> {
333333
final TreePath path = tree.getSelectionPath();
334334
if (path == null) {
@@ -346,6 +346,24 @@ private void addContextualMenuToTree() {
346346
}
347347
});
348348
popup.add(jmi);
349+
jmi = new JMenuItem("Open in Terminal");
350+
jmi.addActionListener(e -> {
351+
final TreePath path = tree.getSelectionPath();
352+
if (path == null) {
353+
JOptionPane.showMessageDialog(this, "No items are currently selected.", "Invalid Selection",
354+
JOptionPane.INFORMATION_MESSAGE);
355+
return;
356+
}
357+
try {
358+
final String filepath = (String) ((FileSystemTree.Node) path.getLastPathComponent()).getUserObject();
359+
TextEditor.GuiUtils.openTerminal(new File(filepath));
360+
} catch (final Exception ignored) {
361+
JOptionPane.showMessageDialog(this, "Could not open path in Terminal.", "Error",
362+
JOptionPane.ERROR_MESSAGE);
363+
}
364+
});
365+
popup.add(jmi);
366+
349367
popup.addSeparator();
350368
jmi = new JMenuItem("Reset to Home Folder");
351369
jmi.addActionListener(e -> changeRootPath(System.getProperty("user.home")));
@@ -363,11 +381,6 @@ void changeRootPath(final String path) {
363381
expandImmediateNodes();
364382
}
365383

366-
private void collapseAllNodes() {
367-
for (int i = tree.getRowCount() - 1; i >= 0; i--)
368-
tree.collapseRow(i);
369-
}
370-
371384
private void expandImmediateNodes() {
372385
for (int i = tree.getRowCount() - 1; i >= 0; i--)
373386
tree.expandRow(i);
@@ -387,8 +400,8 @@ private void showHelpMsg() {
387400
+ "to paste their paths into the active script.</p>" //
388401
+ "<br><p><b>Filtering Files</b></p>" //
389402
+ "<p>Filters affect filenames (not folders) and are applied by typing a filtering "//
390-
+ "string + [Enter]. Filters act only on files being listed, and ignore collapsed " //
391-
+ "folders. Examples of regex usage:</p>" //
403+
+ "string + [Enter]. Filters act only on files being listed in expanded directories, " //
404+
+ "and ignore the content of collapsed folders. Examples of regex usage:</p>" //
392405
+ "<br><table align='center'>" //
393406
+ " <tr>" //
394407
+ " <th>Pattern</th>" //

src/main/java/org/scijava/ui/swing/script/GutterUtils.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,18 @@ public int getIconWidth() {
124124

125125
@Override
126126
public void paintIcon(final Component c, final Graphics g, final int x, final int y) {
127-
g.setColor(background);
128-
g.fillRect(x, y, size, size);
129-
g.setColor(foreground);
130-
g.drawRect(x, y, size, size);
131-
g.drawLine(x + 2, y + size / 2, x + 2 + size / 2, y + size / 2);
127+
// the default '+'/'-' symbols are to small with scaled fonts, and we don't
128+
// have an easy way to resize the space allocated for the icon in the gutter
132129
if (collapsed) {
133-
g.drawLine(x + size / 2, y + 2, x + size / 2, y + size / 2 + 2);
130+
g.setColor(foreground);
131+
g.fillRect(x, y, size, size);
132+
g.setColor(background);
133+
g.drawRect(x, y, size-2, size-1); // no idea why the extra pixel is needed
134+
} else {
135+
g.setColor(background);
136+
g.fillRect(x, y, size, size);
137+
g.setColor(foreground);
138+
g.drawRect(x, y, size-2, size-1);
134139
}
135140
}
136141
}

src/main/java/org/scijava/ui/swing/script/OutlineTreePanel.java

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import java.text.AttributedCharacterIterator;
4141
import java.text.AttributedString;
4242

43+
import javax.swing.JCheckBoxMenuItem;
4344
import javax.swing.JMenuItem;
4445
import javax.swing.JPopupMenu;
4546
import javax.swing.JScrollPane;
@@ -63,16 +64,17 @@ class OutlineTreePanel extends JScrollPane {
6364
private AbstractSourceTree sourceTree;
6465
private final Color placeholdColor;
6566
private float fontSize;
67+
private boolean sorted;
68+
private boolean major;
6669

6770
OutlineTreePanel(final TextEditor editor) {
6871
super();
6972
fontSize = getFont().getSize();
7073
setViewportView(new UnsupportedLangTree());
71-
placeholdColor = TextEditor.getDisabledComponentColor();
72-
74+
placeholdColor = TextEditor.GuiUtils.getDisabledComponentColor();
7375
}
7476

75-
protected void refreshSourceTree(final EditorPane pane) {
77+
protected void rebuildSourceTree(final EditorPane pane) {
7678
if (!isVisible())
7779
return;
7880
if (sourceTree != null) {
@@ -82,14 +84,15 @@ protected void refreshSourceTree(final EditorPane pane) {
8284
final String language = (sLanguage == null) ? "" : sLanguage.getLanguageName();
8385
switch (language) {
8486
case "Java":
85-
sourceTree = new JavaOutlineTree();
87+
//case "BeanShell":
88+
sourceTree = new JavaOutlineTree(sorted);
8689
break;
8790
case "JavaScript":
88-
sourceTree = new JavaScriptOutlineTree();
91+
sourceTree = new JavaScriptOutlineTree(sorted);
8992
break;
9093
default:
9194
if (EditorPane.SYNTAX_STYLE_XML.equals(pane.getSyntaxEditingStyle())) {
92-
sourceTree = new XmlOutlineTree();
95+
sourceTree = new XmlOutlineTree(sorted);
9396
} else
9497
sourceTree = null;
9598
break;
@@ -98,6 +101,7 @@ protected void refreshSourceTree(final EditorPane pane) {
98101
if (sourceTree == null) {
99102
setViewportView(new UnsupportedLangTree(pane));
100103
} else {
104+
sourceTree.setShowMajorElementsOnly(major);
101105
sourceTree.setFont(sourceTree.getFont().deriveFont(fontSize));
102106
sourceTree.listenTo(pane);
103107
setViewportView(sourceTree);
@@ -108,10 +112,35 @@ protected void refreshSourceTree(final EditorPane pane) {
108112

109113
private void setPopupMenu(final JTree tree, final EditorPane pane) {
110114
final JPopupMenu popup = new JPopupMenu();
111-
final JMenuItem jmi = new JMenuItem("Refresh");
112-
jmi.addActionListener(e -> refreshSourceTree(pane));
115+
JMenuItem jmi;
116+
if (tree instanceof AbstractSourceTree) {
117+
jmi = new JMenuItem("Collapse All");
118+
jmi.addActionListener(e -> TextEditor.GuiUtils.collapseAllTreeNodes(tree));
119+
popup.add(jmi);
120+
jmi = new JMenuItem("Expand All");
121+
jmi.addActionListener(e -> TextEditor.GuiUtils.expandAllTreeNodes(tree));
122+
popup.add(jmi);
123+
popup.addSeparator();
124+
final JCheckBoxMenuItem jcmi1 = new JCheckBoxMenuItem("Hide 'Minor' Elements", major);
125+
jcmi1.setToolTipText("Whether non-proeminent elements (e.g., local variables) should be displayed");
126+
jcmi1.addItemListener(e -> {
127+
major = jcmi1.isSelected();
128+
((AbstractSourceTree) tree).setShowMajorElementsOnly(major);
129+
((AbstractSourceTree) tree).refresh();
130+
});
131+
popup.add(jcmi1);
132+
final JCheckBoxMenuItem jcmi2 = new JCheckBoxMenuItem("Sort Elements", sorted);
133+
jcmi2.addItemListener(e -> {
134+
sorted = jcmi2.isSelected();
135+
((AbstractSourceTree) tree).setSorted(sorted); // will refresh
136+
});
137+
popup.add(jcmi2);
138+
popup.addSeparator();
139+
}
140+
jmi = new JMenuItem("Rebuild");
141+
jmi.addActionListener(e -> rebuildSourceTree(pane));
113142
popup.add(jmi);
114-
setComponentPopupMenu(popup);
143+
tree.setComponentPopupMenu(popup);
115144
}
116145

117146
private class UnsupportedLangTree extends JTree {
@@ -124,7 +153,7 @@ public UnsupportedLangTree() {
124153
super((TreeNode) null);
125154
}
126155

127-
public UnsupportedLangTree(EditorPane pane) {
156+
public UnsupportedLangTree(final EditorPane pane) {
128157
this();
129158
setPopupMenu(this, pane);
130159
}

0 commit comments

Comments
 (0)