@@ -514,13 +514,6 @@ private JTabbedPane getTabbedPane() {
514514 tabbedPane .addChangeListener (e -> {
515515 final JTabbedPane source = (JTabbedPane ) e .getSource ();
516516 final int selectedTab = source .getSelectedIndex ();
517- // Do not allow secondary tabs to be selected while operations are pending
518- if (selectedTab > 0 && userInteractionConstrained ()) {
519- tabbedPane .setSelectedIndex (0 );
520- guiUtils .blinkingError (statusText ,
521- "Please complete current task before selecting the " + source .getTitleAt (selectedTab ) +" tab." );
522- return ;
523- }
524517 if (recorder != null ) {
525518 recorder .recordCmd ("snt.getUI().selectTab(\" " + source .getTitleAt (selectedTab ) +"\" )" );
526519 }
@@ -548,16 +541,6 @@ public SNTPrefs getPrefs() {
548541 return plugin .getPrefs ();
549542 }
550543
551- private boolean userInteractionConstrained () {
552- if (plugin .getPrefs ().getTemp ("demo-running" , false ))
553- return false ;
554- return switch (getState ()) {
555- case PARTIAL_PATH , SEARCHING , QUERY_KEEP , RUNNING_CMD , CALCULATING_HESSIAN_I , CALCULATING_HESSIAN_II ,
556- WAITING_FOR_SIGMA_POINT_I , WAITING_FOR_SIGMA_CHOICE , LOADING -> true ;
557- default -> false ;
558- };
559- }
560-
561544 /**
562545 * Assesses whether the UI is blocked.
563546 *
@@ -623,24 +606,22 @@ public void runCommand(final String cmd) throws IllegalArgumentException {
623606 */
624607 public void selectTab (final String tabTitle ) {
625608 final JTabbedPane tp = getJTabbedPaneAddedToContentPane ();
626- SwingUtilities .invokeLater (() -> {
627- if (tp != null ) {
628- final int idx = tp .indexOfTab (tabTitle .trim ().split (" " )[0 ]);
629- if (idx >= 0 ) {
630- tp .setSelectedIndex (idx );
631- } else {
632- // Try case-insensitive match
633- final String key = tabTitle .trim ().split (" " )[0 ].toLowerCase ();
634- for (int i = 0 ; i < tp .getTabCount (); i ++) {
635- if (tp .getTitleAt (i ).toLowerCase ().startsWith (key )) {
636- tp .setSelectedIndex (i );
637- return ;
638- }
639- }
640- throw new IllegalArgumentException ("Unrecognized tab: " + tabTitle );
641- }
642- }
643- });
609+ final int idx = InternalUtils .getTabIndex (tp , tabTitle );
610+ if (idx != -1 )
611+ SwingUtilities .invokeLater (() -> tp .setSelectedIndex (idx ));
612+ else
613+ throw new IllegalArgumentException ("Unrecognized tab: " + tabTitle );
614+ }
615+
616+ private void selectAndHighlightTab (final String tabTitle ) {
617+ final JTabbedPane tp = getJTabbedPaneAddedToContentPane ();
618+ final int idx = InternalUtils .getTabIndex (tp , tabTitle );
619+ if (idx > -1 ) {
620+ SwingUtilities .invokeLater (() -> {
621+ tp .setSelectedIndex (idx );
622+ InternalUtils .blinkTab (tp , idx );
623+ });
624+ }
644625 }
645626
646627 private JTabbedPane getJTabbedPaneAddedToContentPane () {
@@ -1462,7 +1443,7 @@ protected void loadImagefromGUI(final int newC, final int newT) {
14621443 private JPanel viewsPanel () {
14631444 final JPanel viewsPanel = new JPanel (new GridBagLayout ());
14641445 final GridBagConstraints gdb = GuiUtils .defaultGbc ();
1465- gdb .gridwidth = 1 ;
1446+ gdb .gridwidth = 3 ;
14661447
14671448 final JCheckBox zoomAllPanesCheckBox = new JCheckBox ("Apply zoom changes to all views" ,
14681449 !plugin .isZoomAllPanesDisabled ());
@@ -1587,12 +1568,17 @@ else if (plugin.isDisplayCanvas(imp) && imp.getNDimensions() == 2 && imp.getBitD
15871568 invertLutButton .setPreferredSize (new Dimension (
15881569 invertLutButton .getPreferredSize ().width ,
15891570 rebuildCanvasButton .getPreferredSize ().height ));
1590- final JPanel buttonPanel = new JPanel (new FlowLayout (FlowLayout .LEADING , 0 , InternalUtils .MARGIN ));
1591- buttonPanel .add (invertLutButton );
1592- buttonPanel .add (rebuildCanvasButton );
1593- buttonPanel .add (refreshPanesButton );
1571+ // Disable fill/weightx (set by defaultGbc) so buttons render at their preferred size
15941572 gdb .fill = GridBagConstraints .NONE ;
1595- viewsPanel .add (buttonPanel , gdb );
1573+ gdb .weightx = 0 ;
1574+ gdb .insets = new Insets (InternalUtils .MARGIN , 0 , InternalUtils .MARGIN , 0 );
1575+ gdb .gridwidth = 1 ;
1576+ gdb .gridx = 0 ;
1577+ viewsPanel .add (invertLutButton , gdb );
1578+ gdb .gridx = 1 ;
1579+ viewsPanel .add (rebuildCanvasButton , gdb );
1580+ gdb .gridx = 2 ;
1581+ viewsPanel .add (refreshPanesButton , gdb );
15961582 return viewsPanel ;
15971583 }
15981584
@@ -1772,6 +1758,7 @@ private JPanel assignDiameterPanel() {
17721758 showStatus ("Interactive diameter reset" , true );
17731759 });
17741760 final JPanel p = new JPanel ();
1761+ p .setOpaque (false );
17751762 p .setLayout (new GridBagLayout ());
17761763 final GridBagConstraints c = GuiUtils .defaultGbc ();
17771764 c .fill = GridBagConstraints .HORIZONTAL ;
@@ -1820,8 +1807,7 @@ public void focusLost(final FocusEvent e) {
18201807 recorder .recordCmd ("snt.getUI().setRenderingScale(-1)" );
18211808 showStatus ("Node scale reset" , true );
18221809 });
1823- final JPanel p = new JPanel ();
1824- p .setLayout (new GridBagLayout ());
1810+ final JPanel p = InternalUtils .getGlassPanel ();
18251811 final GridBagConstraints c = GuiUtils .defaultGbc ();
18261812 c .fill = GridBagConstraints .HORIZONTAL ;
18271813 c .gridx = 0 ;
@@ -1849,8 +1835,7 @@ private JPanel transparencyDefPanel() {
18491835 showStatus ("Default transparency reset" , true );
18501836 });
18511837
1852- final JPanel p = new JPanel ();
1853- p .setLayout (new GridBagLayout ());
1838+ final JPanel p = InternalUtils .getGlassPanel ();
18541839 final GridBagConstraints c = GuiUtils .defaultGbc ();
18551840 c .fill = GridBagConstraints .HORIZONTAL ;
18561841 c .gridx = 0 ;
@@ -1879,8 +1864,7 @@ private JPanel transparencyOutOfBoundsPanel() {
18791864 showStatus ("Default transparency reset" , true );
18801865 });
18811866
1882- final JPanel p = new JPanel ();
1883- p .setLayout (new GridBagLayout ());
1867+ final JPanel p = InternalUtils .getGlassPanel ();
18841868 final GridBagConstraints c = GuiUtils .defaultGbc ();
18851869 c .fill = GridBagConstraints .HORIZONTAL ;
18861870 c .gridx = 0 ;
@@ -1952,8 +1936,7 @@ private JPanel extraColorsPanel() {
19521936 setColor (key , color );
19531937 cChooser .setSelectedColor (color , true );
19541938 });
1955- final JPanel p = new JPanel ();
1956- p .setLayout (new GridBagLayout ());
1939+ final JPanel p = InternalUtils .getGlassPanel ();
19571940 final GridBagConstraints gbcLabel = new GridBagConstraints ();
19581941 p .add (GuiUtils .leftAlignedLabel ("Colors: " , true ), gbcLabel );
19591942 final GridBagConstraints gbcComboBox = new GridBagConstraints ();
@@ -4773,12 +4756,15 @@ private void registerTabCommandFinderAliases() {
47734756 "Main/Home Tab" ,
47744757 List .of ("Tabs" ),
47754758 List .of ("data source" , "cursor snapping" , "display filters" , "interactive tracing" , "channel" , "frame" ),
4776- () -> selectTab ("Main" ));
4759+ () -> selectTab ("Main" ),
4760+ () -> selectAndHighlightTab ("Main" ));
47774761 // Options tab
47784762 commandFinder .registerKeywords (
47794763 "Options" ,
47804764 List .of ("Tabs" ),
4781- List .of ("settings" ), () -> selectTab ("Options" ));
4765+ List .of ("settings" ),
4766+ () -> selectTab ("Options" ),
4767+ () -> selectAndHighlightTab ("Options" ));
47824768 // Curation Assistant
47834769 final List <String > qcKeywords = new ArrayList <>(List .of ("QC" , "validation" , "warning" , "issues" , "plausibility" ,
47844770 "deep scan" , "live monitor" ));
@@ -4787,37 +4773,44 @@ private void registerTabCommandFinderAliases() {
47874773 commandFinder .registerKeywords (
47884774 "Curation Assistant" ,
47894775 List .of ("Tabs" ),
4790- qcKeywords , () -> selectTab ("Assistant" ));
4776+ qcKeywords ,
4777+ () -> selectTab ("Assistant" ),
4778+ () -> selectAndHighlightTab ("Assistant" ));
47914779 // Bookmarks tab
47924780 commandFinder .registerKeywords (
47934781 "Bookmarks" ,
47944782 List .of ("Tabs" ),
47954783 List .of ("bookmark" , "marker" , "tagged location" , "tagged node" , "colocalize" , "location" ,
47964784 "click-on-point" , "export csv" ), //
4797- () -> selectTab ("Bookmarks" ));
4785+ () -> selectTab ("Bookmarks" ),
4786+ () -> selectAndHighlightTab ("Bookmarks" ));
47984787 commandFinder .registerKeywords (
47994788 "3D" ,
48004789 List .of ("Tabs" ),
48014790 List .of ("viewer" , "big data" , "bvv" , "bdv" , "sciview" , "reconstruction" , "rec viewer" , "legacy" ),
4802- () -> selectTab ("3D" ));
4791+ () -> selectTab ("3D" ),
4792+ () -> selectAndHighlightTab ("3D" ));
48034793 // Delineations tab
48044794 commandFinder .registerKeywords (
48054795 "Delineations" ,
48064796 List .of ("Tabs" ),
48074797 List .of ("delineation" , "region" , "atlas annotation" , "assignment" , "labels/mask image" , "roi" ),
4808- () -> selectTab ("Delineations" ));
4798+ () -> selectTab ("Delineations" ),
4799+ () -> selectAndHighlightTab ("Delineations" ));
48094800 // Seeds tab
48104801 commandFinder .registerKeywords (
4811- "Seeds " ,
4802+ "Seeded Tracing " ,
48124803 List .of ("Tabs" ),
48134804 List .of ("seed" , "soma" , "import csv" , "labels/mask image" , "autotrace" , "candidate points" , "prediction" , "waypoint" ),
4814- () -> selectTab ("Seeds" ));
4805+ () -> selectTab ("Seeds" ),
4806+ () -> selectAndHighlightTab ("Seeds" ));
48154807 // Notes tab
48164808 commandFinder .registerKeywords (
4817- "Notes " ,
4809+ "Notepad " ,
48184810 List .of ("Tabs" ),
4819- List .of ("note" , "comment" , "record" ),
4820- () -> selectTab ("Notes" ));
4811+ List .of ("notes" , "comment" , "record" ),
4812+ () -> selectTab ("Notes" ),
4813+ () -> selectAndHighlightTab ("Notes" ));
48214814 }
48224815
48234816 /** Returns the Curation Manager (Curation Assistant). */
@@ -5607,6 +5600,47 @@ static JPanel getTab() {
56075600 return tab ;
56085601 }
56095602
5603+ static JPanel getGlassPanel () {
5604+ final JPanel p = new JPanel ();
5605+ p .setLayout (new GridBagLayout ());
5606+ p .setOpaque (false );
5607+ return p ;
5608+ }
5609+
5610+ static int getTabIndex (final JTabbedPane tabbedPane , final String tabTitle ) {
5611+ if (tabbedPane == null ) return -1 ;
5612+ int idx = tabbedPane .indexOfTab (tabTitle .trim ().split (" " )[0 ]);
5613+ if (idx > -1 ) return idx ;
5614+ // Try case-insensitive match
5615+ final String key = tabTitle .trim ().split (" " )[0 ].toLowerCase ();
5616+ for (int i = 0 ; i < tabbedPane .getTabCount (); i ++) {
5617+ if (tabbedPane .getTitleAt (i ).toLowerCase ().startsWith (key )) {
5618+ return i ;
5619+ }
5620+ }
5621+ return -1 ;
5622+ }
5623+
5624+ private static void blinkTab (final JTabbedPane tabbedPane , final int tabIndex ) {
5625+ if (tabIndex == -1 ) return ;
5626+ final Color originalColor = tabbedPane .getBackgroundAt (tabIndex );
5627+ final Timer blinkTimer = new Timer (500 , null );
5628+ blinkTimer .addActionListener (new ActionListener () {
5629+ int currentTick = 0 ;
5630+ @ Override
5631+ public void actionPerformed (final ActionEvent e ) {
5632+ if (++currentTick > 4 ) {
5633+ blinkTimer .stop ();
5634+ tabbedPane .setBackgroundAt (tabIndex , originalColor );
5635+ return ;
5636+ }
5637+ tabbedPane .setBackgroundAt (tabIndex ,
5638+ (currentTick % 2 == 1 ) ? GuiUtils .warningColor () : originalColor );
5639+ }
5640+ });
5641+ blinkTimer .start ();
5642+ }
5643+
56105644 static void ijmLogMessage () {
56115645 if (ij .IJ .recording ()) {
56125646 final String recordString = """
0 commit comments