From 58dbccec2d5264cd2ce0317e49f83c766bd562b7 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Wed, 25 May 2022 11:18:22 +0200 Subject: [PATCH] Table: optionally paint alternating rows below table if table is smaller than scroll pane (issue #504) --- CHANGELOG.md | 5 ++- .../com/formdev/flatlaf/ui/FlatTableUI.java | 37 ++++++++++++++++++- .../formdev/flatlaf/ui/FlatViewportUI.java | 33 ++++++++++++----- .../flatlaf/testing/FlatComponents2Test.java | 24 ++++++++++++ .../flatlaf/testing/FlatComponents2Test.jfd | 22 ++++++++++- 5 files changed, 108 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dda9b960..20986486 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ FlatLaf Change Log #### New features and improvements +- Table: Optionally paint alternating rows below table if table is smaller than + scroll pane. Set UI value `Table.paintOutsideAlternateRows` to `true`. + Requires that `Table.alternateRowColor` is set to a color. (issue #504) - ToggleButton: Made the underline placement of tab-style toggle buttons configurable. (PR #530; issue #529) - Added spanish translation. (PR #525) @@ -155,7 +158,7 @@ FlatLaf Change Log - Possibility to hide window title bar icon (for single window set client property `JRootPane.titleBarShowIcon` to `false`; for all windows set UI value `TitlePane.showIcon` to `false`). - - OptionPane: Hide window title bar icon by default. Can be be made visibly by + - OptionPane: Hide window title bar icon by default. Can be made visibly by setting UI default `OptionPane.showIcon` to `true`. (issue #416) - No longer show the Java "duke/cup" icon if no window icon image is set. (issue #416) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableUI.java index 44269bd4..1fec78a8 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableUI.java @@ -80,6 +80,7 @@ import com.formdev.flatlaf.util.UIScale; * @uiDefault Table.intercellSpacing Dimension * @uiDefault Table.selectionInactiveBackground Color * @uiDefault Table.selectionInactiveForeground Color + * @uiDefault Table.paintOutsideAlternateRows boolean * * * @@ -95,7 +96,7 @@ import com.formdev.flatlaf.util.UIScale; */ public class FlatTableUI extends BasicTableUI - implements StyleableUI + implements StyleableUI, FlatViewportUI.ViewportPainter { protected boolean showHorizontalLines; protected boolean showVerticalLines; @@ -421,4 +422,38 @@ public class FlatTableUI ? (viewport != rowHeader) : (viewport == rowHeader || rowHeader == null); } + + /** @since 2.3 */ + @Override + public void paintViewport( Graphics g, JComponent c, JViewport viewport ) { + int viewportWidth = viewport.getWidth(); + int viewportHeight = viewport.getHeight(); + + // fill viewport background in same color as table background + if( viewport.isOpaque() ) { + g.setColor( table.getBackground() ); + g.fillRect( 0, 0, viewportWidth, viewportHeight ); + } + + // paint alternating empty rows + boolean paintOutside = UIManager.getBoolean( "Table.paintOutsideAlternateRows" ); + Color alternateColor; + if( paintOutside && (alternateColor = UIManager.getColor( "Table.alternateRowColor" )) != null ) { + g.setColor( alternateColor ); + + int rowCount = table.getRowCount(); + + // paint alternating empty rows below the table + int tableHeight = table.getHeight(); + if( tableHeight < viewportHeight ) { + int tableWidth = table.getWidth(); + int rowHeight = table.getRowHeight(); + + for( int y = tableHeight, row = rowCount; y < viewportHeight; y += rowHeight, row++ ) { + if( row % 2 != 0 ) + g.fillRect( 0, y, tableWidth, rowHeight ); + } + } + } + } } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatViewportUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatViewportUI.java index 89167367..d844725e 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatViewportUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatViewportUI.java @@ -18,8 +18,8 @@ package com.formdev.flatlaf.ui; import java.awt.Component; import java.awt.Graphics; +import java.lang.reflect.Method; import javax.swing.JComponent; -import javax.swing.JTable; import javax.swing.JViewport; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicViewportUI; @@ -43,15 +43,28 @@ public class FlatViewportUI } @Override - public void update( Graphics g, JComponent c ) { - Component view = ((JViewport)c).getView(); - if( c.isOpaque() && view instanceof JTable ) { - // paint viewport background in same color as table background - g.setColor( view.getBackground() ); - g.fillRect( 0, 0, c.getWidth(), c.getHeight() ); + public void paint( Graphics g, JComponent c ) { + super.paint( g, c ); - paint( g, c ); - } else - super.update( g, c ); + Component view = ((JViewport)c).getView(); + if( view instanceof JComponent ) { + try { + Method m = view.getClass().getMethod( "getUI" ); + Object ui = m.invoke( view ); + if( ui instanceof ViewportPainter ) + ((ViewportPainter)ui).paintViewport( g, (JComponent) view, (JViewport) c ); + } catch( Exception ex ) { + // ignore + } + } + } + + //---- interface ViewportPainter ------------------------------------------ + + /** + * @since 2.3 + */ + public interface ViewportPainter { + void paintViewport( Graphics g, JComponent c, JViewport viewport ); } } diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponents2Test.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponents2Test.java index 54aae794..ac67ccfe 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponents2Test.java +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponents2Test.java @@ -336,6 +336,16 @@ public class FlatComponents2Test table.setSurrendersFocusOnKeystroke( focusCellEditorCheckBox.isSelected() ); } + private void alternatingRowsChanged() { + UIManager.put( "Table.alternateRowColor", alternatingRowsCheckBox.isSelected() ? Color.orange : null ); + table1ScrollPane.repaint(); + } + + private void paintOutsideAlternateRowsChanged() { + UIManager.put( "Table.paintOutsideAlternateRows", paintOutsideAlternateRowsCheckBox.isSelected() ? true : null ); + table1ScrollPane.repaint(); + } + private void treeRendererChanged() { Object sel = treeRendererComboBox.getSelectedItem(); if( !(sel instanceof String) ) @@ -493,8 +503,10 @@ public class FlatComponents2Test focusCellEditorCheckBox = new JCheckBox(); showVerticalLinesCheckBox = new JCheckBox(); columnSelectionCheckBox = new JCheckBox(); + alternatingRowsCheckBox = new JCheckBox(); intercellSpacingCheckBox = new JCheckBox(); rowHeaderCheckBox = new JCheckBox(); + paintOutsideAlternateRowsCheckBox = new JCheckBox(); redGridColorCheckBox = new JCheckBox(); tableHeaderButtonCheckBox = new JCheckBox(); @@ -875,6 +887,11 @@ public class FlatComponents2Test columnSelectionCheckBox.addActionListener(e -> columnSelectionChanged()); tableOptionsPanel.add(columnSelectionCheckBox, "cell 1 2"); + //---- alternatingRowsCheckBox ---- + alternatingRowsCheckBox.setText("alternating rows"); + alternatingRowsCheckBox.addActionListener(e -> alternatingRowsChanged()); + tableOptionsPanel.add(alternatingRowsCheckBox, "cell 2 2"); + //---- intercellSpacingCheckBox ---- intercellSpacingCheckBox.setText("intercell spacing"); intercellSpacingCheckBox.addActionListener(e -> intercellSpacingChanged()); @@ -885,6 +902,11 @@ public class FlatComponents2Test rowHeaderCheckBox.addActionListener(e -> rowHeaderChanged()); tableOptionsPanel.add(rowHeaderCheckBox, "cell 1 3"); + //---- paintOutsideAlternateRowsCheckBox ---- + paintOutsideAlternateRowsCheckBox.setText("outside alternating rows"); + paintOutsideAlternateRowsCheckBox.addActionListener(e -> paintOutsideAlternateRowsChanged()); + tableOptionsPanel.add(paintOutsideAlternateRowsCheckBox, "cell 2 3"); + //---- redGridColorCheckBox ---- redGridColorCheckBox.setText("red grid color"); redGridColorCheckBox.addActionListener(e -> redGridColorChanged()); @@ -927,8 +949,10 @@ public class FlatComponents2Test private JCheckBox focusCellEditorCheckBox; private JCheckBox showVerticalLinesCheckBox; private JCheckBox columnSelectionCheckBox; + private JCheckBox alternatingRowsCheckBox; private JCheckBox intercellSpacingCheckBox; private JCheckBox rowHeaderCheckBox; + private JCheckBox paintOutsideAlternateRowsCheckBox; private JCheckBox redGridColorCheckBox; private JCheckBox tableHeaderButtonCheckBox; // JFormDesigner - End of variables declaration //GEN-END:variables diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponents2Test.jfd b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponents2Test.jfd index 2ded4803..148f4584 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponents2Test.jfd +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponents2Test.jfd @@ -1,4 +1,4 @@ -JFDML JFormDesigner: "7.0.5.0.382" Java: "16" encoding: "UTF-8" +JFDML JFormDesigner: "7.0.5.0.404" Java: "17.0.2" encoding: "UTF-8" new FormModel { contentType: "form/swing" @@ -485,6 +485,16 @@ new FormModel { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 1 2" } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "alternatingRowsCheckBox" + "text": "alternating rows" + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "alternatingRowsChanged", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 2 2" + } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "intercellSpacingCheckBox" "text": "intercell spacing" @@ -505,6 +515,16 @@ new FormModel { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 1 3" } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "paintOutsideAlternateRowsCheckBox" + "text": "outside alternating rows" + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "paintOutsideAlternateRowsChanged", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 2 3" + } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "redGridColorCheckBox" "text": "red grid color"