Skip to content

Commit 8e50ba4

Browse files
committed
Improve consistency of contextual menus between viewers
1 parent e035b12 commit 8e50ba4

10 files changed

Lines changed: 234 additions & 128 deletions

‎src/main/java/sc/fiji/snt/CurationManager.java‎

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1902,21 +1902,13 @@ private void addCheckRow(final JPanel panel, final GridBagConstraints c,
19021902
private JComponent spinnerWithHistogram(final JSpinner spinner, final JButton histButton) {
19031903
tightenSpinner(spinner);
19041904
if (histButton == null) return spinner;
1905-
// Listeners fire last-added first: commit pending typed text before the plot reads values
1906-
histButton.addActionListener(e -> commitSpinnerEdit(spinner));
19071905
final JPanel p = new JPanel(new BorderLayout(0, 0));
19081906
p.setOpaque(false);
19091907
p.add(spinner, BorderLayout.CENTER);
19101908
p.add(histButton, BorderLayout.EAST);
19111909
return p;
19121910
}
19131911

1914-
/**
1915-
* Reduces a spinner's editor to a compact column count. All curation
1916-
* spinners hold short numeric values (typically 3-5 characters); the
1917-
* default of ~9 columns wastes horizontal space and pushes the checkbox
1918-
* label into truncation.
1919-
*/
19201912
/** Keeps min <= max: each fork angle spinner is bounded by the other's value */
19211913
private void coupleBranchAngleBounds() {
19221914
if (branchAngleMinSpinner == null || branchAngleMaxSpinner == null) return;
@@ -1927,23 +1919,17 @@ private void coupleBranchAngleBounds() {
19271919
}
19281920
}
19291921

1930-
private static void commitSpinnerEdit(final JSpinner spinner) {
1931-
try {
1932-
spinner.commitEdit();
1933-
} catch (final java.text.ParseException ignored) {
1934-
// invalid text: keep last valid value
1935-
}
1936-
}
1937-
1922+
/**
1923+
* Reduces a spinner's editor to a compact column count. All curation
1924+
* spinners hold short numeric values (typically 3-5 characters); the
1925+
* default of ~9 columns wastes horizontal space and pushes the checkbox
1926+
* label into truncation.
1927+
*/
19381928
private static void tightenSpinner(final JSpinner spinner) {
19391929
if (spinner == null) return;
19401930
final JComponent editor = spinner.getEditor();
19411931
if (editor instanceof JSpinner.DefaultEditor de) {
19421932
de.getTextField().setColumns(4);
1943-
// Commit typed values as soon as they are valid: the histogram and
1944-
// Scan buttons are non-focusable, so no focus-lost commit happens
1945-
if (de.getTextField().getFormatter() instanceof javax.swing.text.DefaultFormatter df)
1946-
df.setCommitsOnValidEdit(true);
19471933
}
19481934
}
19491935

‎src/main/java/sc/fiji/snt/InteractiveTracerCanvas.java‎

Lines changed: 11 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -385,35 +385,13 @@ private void updateConnectToSecondaryEditingPathMenuItem() {
385385
}
386386

387387
private JMenuItem helpOnConnectingMenuItem() {
388-
final String msg = "<HTML>To connect two paths in <i>Edit Mode</i>:<ol>" +
389-
" <li>Press <tt>Shift+E</tt> to enter Edit Mode</li>" +
390-
" <li>Select the <b>parent</b> path using <tt>G</tt> (<u>G</u>roup paths around cursor)</li>" +
391-
" <li>Hover over the node where the child should branch from</li>" +
392-
" <li>Press <tt>G</tt> to switch to the <b>child</b> path</li>" +
393-
" <li>Select the child's connection node (typically a start or end node)</li>" +
394-
" <li>Press <tt>C</tt> to <u>C</u>onnect</li>" +
395-
"</ol>Notes:<ul>" +
396-
" <li>A confirmation dialog shows the parent/child assignment with option to swap roles</li>" +
397-
" <li>Child paths are automatically re-oriented to maintain proper tree structure</li>" +
398-
" <li>If connection points are distant, a bridging segment is created automatically</li>" +
399-
" <li>If child's selected node is mid-path, a T-junction is created</li>" +
400-
" <li>Loop-forming connections are not allowed</li>" +
401-
" <li>To concatenate or combine paths end-to-end, use Path Manager's Edit menu</li>" +
402-
"</ul>";
403-
final JMenuItem helpItem = new JMenuItem(AListener.NODE_CONNECT_HELP);
404-
helpItem.addActionListener(e -> {
405-
final boolean canvasActivationState = tracerPlugin.getPrefs().isCanvasAutoActivationEnabled();
406-
tracerPlugin.getPrefs().setCanvasAutoActivation(false); // this will not update the checkbox state in SNTUI, but
407-
// ensures the help dialog will maintain its frontmost state
408-
getGuiUtils().showHTMLDialog(msg, AListener.NODE_CONNECT_HELP, false)
409-
.addWindowListener(new WindowAdapter() {
410-
@Override
411-
public void windowClosing(final WindowEvent e) {
412-
tracerPlugin.getPrefs().setCanvasAutoActivation(canvasActivationState);
413-
}
414-
});
415-
});
416-
return helpItem;
388+
final boolean[] canvasActivationState = new boolean[1];
389+
return GuiUtils.MenuItems.connectHelp(this::getGuiUtils, () -> {
390+
canvasActivationState[0] = tracerPlugin.getPrefs().isCanvasAutoActivationEnabled();
391+
// this will not update the checkbox state in SNTUI, but ensures the help dialog will maintain its
392+
// frontmost state
393+
tracerPlugin.getPrefs().setCanvasAutoActivation(false);
394+
}, () -> tracerPlugin.getPrefs().setCanvasAutoActivation(canvasActivationState[0]));
417395
}
418396

419397
/** Menu item for a command shared with the Bdv/Bvv context menu: label and icon come from the enum */
@@ -1454,7 +1432,6 @@ protected void toggleEditMode() {
14541432
toggleEditModeMenuItem.doClick();
14551433
}
14561434

1457-
14581435
private void toggleKeyWarning(final String key, final String msg) {
14591436
final Timer timer = new Timer(500, ae -> {
14601437
// Restore state after delay
@@ -1473,12 +1450,7 @@ private void toggleKeyWarning(final String key, final String msg) {
14731450
PathNodeCanvas.setShowDirectionArrows(true);
14741451
tracerPlugin.repaintAllPanes();
14751452
}
1476-
// Show tip if not suppressed
1477-
if (!tracerPlugin.getPrefs().getTemp("key-skipnag" + key, false)) {
1478-
final Boolean skipNag = getGuiUtils().getPersistentWarning(
1479-
"Tip: Hold \"" + key + "\" to " + msg + ".", "Keyboard Operation");
1480-
if (skipNag != null) tracerPlugin.getPrefs().setTemp("key-skipnag" + key, skipNag);
1481-
}
1453+
getGuiUtils().showKeyTip(tracerPlugin.getPrefs(), key, msg);
14821454

14831455
timer.start();
14841456
}
@@ -1522,7 +1494,7 @@ private class AListener implements ActionListener, ItemListener {
15221494
private final static String INDENT = NodeEditCommand.MENU_INDENT;
15231495
private final static String NODE_MOVE_Z = INDENT + "Bring Active Node to Current Z-plane";
15241496
private final static String NODE_PAINT = INDENT + "Paint Mode";
1525-
private final static String NODE_CONNECT_HELP = INDENT + "Connect to Help...";
1497+
private final static String NODE_CONNECT_HELP = GuiUtils.MenuItems.CONNECT_HELP_LABEL;
15261498
private final static String NODE_CONNECT_TO_PREV_EDITING_PATH_PREFIX = INDENT + "Connect to ";
15271499
private final static String NODE_CONNECT_TO_PREV_EDITING_PATH_PLACEHOLDER = NODE_CONNECT_TO_PREV_EDITING_PATH_PREFIX
15281500
+ "...";
@@ -1604,10 +1576,10 @@ private void handleExtendPath() {
16041576
private boolean handleGeneralCommands(final String command, final ActionEvent e) {
16051577
switch (command) {
16061578
case SHOW_ARROWS:
1607-
toggleKeyWarning("O", "show path orientation (start → end)");
1579+
toggleKeyWarning("O", ContextMenuCommand.ORIENTATION_KEY_TIP);
16081580
return true;
16091581
case HIDE_ALL:
1610-
toggleKeyWarning("H", "temporarily hide paths");
1582+
toggleKeyWarning("H", ContextMenuCommand.HIDE_KEY_TIP);
16111583
return true;
16121584
case BOOKMARK_CURSOR:
16131585
bookmarkCursorLocation();

‎src/main/java/sc/fiji/snt/gui/ContextMenuCommand.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ public enum ContextMenuCommand {
5151
PAUSE_TRACING(Labels.PAUSE_TRACING, '\uf04c', true, false),
5252
PAUSE_SNT(Labels.PAUSE_SNT, IconFactory.GLYPH.PAUSE2);
5353

54+
/** Hold-key descriptions used by {@link GuiUtils#showKeyTip(sc.fiji.snt.SNTPrefs, String, String)} */
55+
public static final String HIDE_KEY_TIP = "temporarily hide paths";
56+
public static final String ORIENTATION_KEY_TIP = "show path orientation (start \u2192 end)";
57+
5458
/** The labels as compile-time constants, for front-ends that dispatch on them (e.g., in a {@code switch}) */
5559
public static final class Labels {
5660
public static final String GRAB = "Grab Nearest Path";

‎src/main/java/sc/fiji/snt/gui/GuiUtils.java‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,21 @@ public boolean[] getConfirmationAndOption(final String msg, final String title,
872872

873873
}
874874

875+
/**
876+
* Displays the "Hold [key] to..." keyboard tip (shared by the InteractiveTracerCanvas and Bdv/Bvv context
877+
* menus), unless the user opted out of it. The opt-out is remembered for the session
878+
*
879+
* @param prefs the preferences holding the opt-out flag
880+
* @param key the key to be held, e.g., "H"
881+
* @param msg what the key does, e.g., "temporarily hide paths"
882+
*/
883+
public void showKeyTip(final SNTPrefs prefs, final String key, final String msg) {
884+
final String pref = "key-skipnag" + key;
885+
if (prefs.getTemp(pref, false)) return;
886+
final Boolean skipNag = getPersistentWarning("Tip: Hold \"" + key + "\" to " + msg + ".", "Keyboard Operation");
887+
if (skipNag != null) prefs.setTemp(pref, skipNag);
888+
}
889+
875890
/**
876891
* Displays a dialog with radio button choices, optional info text, and an optional checkbox.
877892
* Useful for operations that require a selection with additional context and options.
@@ -3576,6 +3591,52 @@ public void windowDeactivated(final WindowEvent e) {
35763591
/** Utility methods for building and configuring {@link JMenuItem}/{@link JMenu} entries. */
35773592
public static class MenuItems {
35783593

3594+
/** Label of the item returned by {@link #connectHelp(Supplier, Runnable, Runnable)} */
3595+
public static final String CONNECT_HELP_LABEL = NodeEditCommand.MENU_INDENT + "Connect to Help...";
3596+
3597+
private static final String CONNECT_HELP_HTML = "<HTML>To connect two paths in <i>Edit Mode</i>:<ol>"
3598+
+ " <li>Press <tt>Shift+E</tt> to enter Edit Mode</li>"
3599+
+ " <li>Select the <b>parent</b> path using <tt>G</tt> (<u>G</u>rab path around cursor)</li>"
3600+
+ " <li>Hover over the node where the child should branch from</li>"
3601+
+ " <li>Press <tt>G</tt> to switch to the <b>child</b> path</li>"
3602+
+ " <li>Select the child's connection node (typically a start or end node)</li>"
3603+
+ " <li>Press <tt>C</tt> to <u>C</u>onnect</li>"
3604+
+ "</ol>Notes:<ul>"
3605+
+ " <li>A confirmation dialog shows the parent/child assignment with option to swap roles</li>"
3606+
+ " <li>Child paths are automatically re-oriented to maintain proper tree structure</li>"
3607+
+ " <li>If connection points are distant, a bridging segment is created automatically</li>"
3608+
+ " <li>If child's selected node is mid-path, a T-junction is created</li>"
3609+
+ " <li>Loop-forming connections are not allowed</li>"
3610+
+ " <li>To concatenate or combine paths end-to-end, use Path Manager's Edit menu</li>"
3611+
+ "</ul>";
3612+
3613+
/**
3614+
* Menu item that opens the "how to connect two paths in Edit Mode" help dialog (shared by the
3615+
* InteractiveTracerCanvas and Bdv/Bvv context menus)
3616+
*
3617+
* @param gui supplies the GuiUtils used to display the dialog (evaluated when the item is activated)
3618+
* @param onOpen optional task run right before the dialog is displayed (may be null)
3619+
* @param onClose optional task run when the dialog is closed (may be null)
3620+
* @return the menu item
3621+
*/
3622+
public static JMenuItem connectHelp(final Supplier<GuiUtils> gui, final Runnable onOpen,
3623+
final Runnable onClose) {
3624+
final JMenuItem helpItem = new JMenuItem(CONNECT_HELP_LABEL);
3625+
helpItem.addActionListener(e -> {
3626+
if (onOpen != null) onOpen.run();
3627+
final JDialog dialog = gui.get().showHTMLDialog(CONNECT_HELP_HTML, CONNECT_HELP_LABEL, false);
3628+
if (onClose != null) {
3629+
dialog.addWindowListener(new WindowAdapter() {
3630+
@Override
3631+
public void windowClosing(final WindowEvent we) {
3632+
onClose.run();
3633+
}
3634+
});
3635+
}
3636+
});
3637+
return helpItem;
3638+
}
3639+
35793640
public static void addSeparator(final JPopupMenu menu, final String header) {
35803641
final JLabel label = GuiUtils.leftAlignedLabel(header, false);
35813642
if (menu.getComponentCount() > 0) menu.addSeparator();

‎src/main/java/sc/fiji/snt/gui/NodeEditCommand.java‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,6 @@ public static void addMenuItems(final JPopupMenu menu, final MenuState state,
178178
final Consumer<NodeEditCommand> handler) {
179179
for (final NodeEditCommand cmd : values()) {
180180
if (cmd == UNDO) continue; // shared with the tracing 'Undo Last Segment' entry (mode-aware)
181-
if (cmd == RADIUS || cmd == SET_ROOT) menu.addSeparator();
182181
final JMenuItem item = cmd.createMenuItem(e -> handler.accept(cmd));
183182
item.setText(MENU_INDENT + cmd.label(state.nodeLocked()));
184183
item.setEnabled(state.isEnabled(cmd));

0 commit comments

Comments
 (0)