@@ -1948,21 +1948,22 @@ public static JDialog showAboutDialog() {
19481948 // All info rows below are borderless JButtons (not JLabels styled to look
19491949 // clickable) so they share the same margin/insets and stay left-aligned
19501950 final String details = getImageJVersion () + " · Java " + System .getProperty ("java.version" );
1951- final JButton ijDetails = infoButton (details , IconFactory .menuIcon ('\ue4e5' , true , IconFactory .secondaryColor ()));
1951+ final Icon refIcon = IconFactory .buttonIcon ('\ue4e5' , true , IconFactory .secondaryColor ());
1952+ final JButton ijDetails = infoButton (details , refIcon , refIcon );
19521953 ijDetails .setToolTipText ("Click to display detailed system information" );
19531954 side .add (ijDetails , gbc );
19541955 gbc .gridy ++;
19551956
19561957 final int threads = SNTPrefs .getThreads ();
19571958 final int cores = Runtime .getRuntime ().availableProcessors ();
1958- final JButton coresDetails = infoButton (threads + "/" + cores + " cores" , GLYPH .MICROCHIP );
1959+ final JButton coresDetails = infoButton (threads + "/" + cores + " cores" , GLYPH .MICROCHIP , refIcon );
19591960 coresDetails .setToolTipText ("Threads used by SNT / cores available to the JVM.\n Click to change" );
19601961 coresDetails .addActionListener (e -> SNTUtils .getContext ().getService (CommandService .class ).run (PrefsCmd .class , true ));
19611962 side .add (coresDetails , gbc );
19621963 gbc .gridy ++;
19631964
19641965 final SNTUtils .HeapInfo heapInfo = SNTUtils .getHeapInfo ();
1965- final JButton heapDetails = infoButton (heapInfo .usedMB () + "/" + heapInfo .maxMB () + " MB memory" , GLYPH .GAUGE );
1966+ final JButton heapDetails = infoButton (heapInfo .usedMB () + "/" + heapInfo .maxMB () + " MB memory" , GLYPH .GAUGE , refIcon );
19661967 heapDetails .setToolTipText ("Used/max heap memory.\n Click to open ImageJ's memory monitor" );
19671968 heapDetails .addActionListener (e -> {
19681969 try {
@@ -1973,7 +1974,7 @@ public static JDialog showAboutDialog() {
19731974 side .add (heapDetails , gbc );
19741975 gbc .gridy ++;
19751976
1976- final JButton cacheDetails = infoButton ("Cache: calculating..." , GLYPH .DATABASE );
1977+ final JButton cacheDetails = infoButton ("Cache: calculating..." , GLYPH .DATABASE , refIcon );
19771978 cacheDetails .setToolTipText ("Click to open\n " + SNTUtils .getCacheDir ().getAbsolutePath ());
19781979 cacheDetails .addActionListener (e -> {
19791980 try {
@@ -1985,7 +1986,7 @@ public static JDialog showAboutDialog() {
19851986 side .add (cacheDetails , gbc );
19861987 gbc .gridy ++;
19871988
1988- final JButton gpuDetails = infoButton ("Max OpenGL: querying..." , GLYPH .CUBE );
1989+ final JButton gpuDetails = infoButton ("Max OpenGL: querying..." , GLYPH .CUBE , refIcon );
19891990 gpuDetails .setEnabled (false ); // enabled once GLUtils.getInfo() resolves, below
19901991 side .add (gpuDetails , gbc );
19911992 gbc .gridy ++;
@@ -2070,23 +2071,23 @@ private static void reportTempMsgOnButtonLabel(final AbstractButton button, fina
20702071 }
20712072
20722073 /** Borderless, zero-margin, left-aligned icon+text button used for the About dialog's info rows */
2073- private static JButton infoButton (final String text , final Icon icon ) {
2074- final JButton button = new JButton (text , icon );
2074+ private static JButton infoButton (final String text , final Icon icon , final Icon refIcon ) {
2075+ final JButton button = new JButton (text , IconFactory . fixedWidthIcon ( icon , refIcon ) );
20752076 button .setForeground (IconFactory .secondaryColor ());
20762077 Buttons .makeBorderless (button );
20772078 button .setHorizontalAlignment (SwingConstants .LEFT );
20782079 button .setMargin (new Insets (0 , 0 , 0 , 0 ));
20792080 return button ;
20802081 }
20812082
2082- private static JButton infoButton (final String text , final GLYPH glyph ) {
2083- return infoButton (text , IconFactory .menuIcon (glyph , IconFactory .secondaryColor ()) );
2083+ private static JButton infoButton (final String text , final GLYPH glyph , final Icon refIcon ) {
2084+ return infoButton (text , IconFactory .buttonIcon (glyph , IconFactory .secondaryColor (), 1f ), refIcon );
20842085 }
20852086
20862087 /** Borderless icon+text button that opens {@code url} when clicked */
20872088 private static JButton urlButton (final String label , final String url , final GLYPH glyph ) {
20882089 final Color c = SNTColor .average (List .of (IconFactory .defaultColor (), IconFactory .selectedColor ()));
2089- final JButton button = new JButton (label , IconFactory .menuIcon (glyph , c ));
2090+ final JButton button = new JButton (label , IconFactory .buttonIcon (glyph , c , 1f ));
20902091 button .setForeground (c );
20912092 Buttons .makeBorderless (button );
20922093 button .addActionListener (e -> openURL (url ));
@@ -4218,6 +4219,38 @@ public Component getTableCellRendererComponent(final JTable t, final Object valu
42184219 }
42194220 }
42204221
4222+ /**
4223+ * Applies a subtle alternating-row background tint to a table (zebra striping), without requiring a custom
4224+ * {@code JTable} subclass. Wraps the default renderer registered for each of the table's column classes so
4225+ * that unselected rows alternate between the table's own background and a slight tint towards its foreground
4226+ * color.
4227+ * <p>
4228+ * Only affects columns that rely on the class-based default renderer (the normal case). A column with a renderer
4229+ * assigned directly via {@link javax.swing.table.TableColumn#setCellRenderer(javax.swing.table.TableCellRenderer)}
4230+ * bypasses the class-based lookup and is left untouched by this method.
4231+ *
4232+ * @param table the table to stripe
4233+ */
4234+ public static void installAlternatingRows (final JTable table ) {
4235+ final double tintWeight = 0.06 ; // small values (0.05-0.08) work best
4236+ final Set <Class <?>> columnClasses = new LinkedHashSet <>();
4237+ for (int i = 0 ; i < table .getColumnCount (); i ++) {
4238+ columnClasses .add (table .getColumnClass (i ));
4239+ }
4240+ for (final Class <?> columnClass : columnClasses ) {
4241+ final javax .swing .table .TableCellRenderer original = table .getDefaultRenderer (columnClass );
4242+ if (original == null ) continue ;
4243+ table .setDefaultRenderer (columnClass , (tbl , value , isSelected , hasFocus , row , column ) -> {
4244+ final Component c = original .getTableCellRendererComponent (tbl , value , isSelected , hasFocus , row , column );
4245+ if (!isSelected ) {
4246+ c .setBackground ((row % 2 == 0 ) ? tbl .getBackground ()
4247+ : SNTColor .mix (tbl .getBackground (), tbl .getForeground (), tintWeight ));
4248+ }
4249+ return c ;
4250+ });
4251+ }
4252+ }
4253+
42214254 /**
42224255 * Auto-sizes the preferred width of the given column to fit its content,
42234256 * with padding.
@@ -4533,7 +4566,7 @@ protected void paintComponent(final Graphics g) {
45334566 *
45344567 * @param table the table to scroll (must be inside a {@link JScrollPane})
45354568 */
4536- private static void scrollToBottom (final JTable table ) {
4569+ public static void scrollToBottom (final JTable table ) {
45374570 SwingUtilities .invokeLater (() -> {
45384571 table .revalidate ();
45394572 SwingUtilities .invokeLater (() -> {
0 commit comments