diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b9e27450..ec43a59f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ FlatLaf Change Log #### New features and improvements +- Table: Support rounded selection. (PR #856) - Button and ToggleButton: Added border colors for pressed and selected states. (issue #848) - Label: Support painting background with rounded corners. (issue #842) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatListUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatListUI.java index 57635398b..a55b91a63 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatListUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatListUI.java @@ -324,8 +324,7 @@ protected void paintCell( Graphics g, int row, Rectangle rowBounds, ListCellRend (rendererComponent instanceof DefaultListCellRenderer || rendererComponent instanceof BasicComboBoxRenderer) && (selectionArc > 0 || - (selectionInsets != null && - (selectionInsets.top != 0 || selectionInsets.left != 0 || selectionInsets.bottom != 0 || selectionInsets.right != 0))) ) + (selectionInsets != null && !FlatUIUtils.isInsetsEmpty( selectionInsets ))) ) { // Because selection painting is done in the cell renderer, it would be // necessary to require a FlatLaf specific renderer to implement rounded selection. @@ -374,7 +373,15 @@ public void fillRect( int x, int y, int width, int height ) { rendererPane.paintComponent( g, rendererComponent, list, cx, rowBounds.y, cw, rowBounds.height, true ); } - /** @since 3 */ + /** + * Paints (rounded) cell selection. + * Supports {@link #selectionArc} and {@link #selectionInsets}. + *
+ * Note: This method is only invoked if either selection arc + * is greater than zero or if selection insets are not empty. + * + * @since 3 + */ protected void paintCellSelection( Graphics g, int row, int x, int y, int width, int height ) { float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight; arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f ); @@ -440,7 +447,8 @@ private boolean useUnitedRoundedSelection( boolean vertical, boolean horizontal * Paints a cell selection at the given coordinates. * The selection color must be set on the graphics context. *
- * This method is intended for use in custom cell renderers.
+ * This method is intended for use in custom cell renderers
+ * to support {@link #selectionArc} and {@link #selectionInsets}.
*
* @since 3
*/
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableCellBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableCellBorder.java
index b22dc6fc5..32cd12138 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableCellBorder.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableCellBorder.java
@@ -65,8 +65,28 @@ public Color getLineColor() {
return super.getLineColor();
}
+ @Override
+ public int getArc() {
+ if( c != null ) {
+ Integer selectionArc = getStyleFromTableUI( c, ui -> ui.selectionArc );
+ if( selectionArc != null )
+ return selectionArc;
+ }
+ return super.getArc();
+ }
+
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
+ if( c != null ) {
+ Insets selectionInsets = getStyleFromTableUI( c, ui -> ui.selectionInsets );
+ if( selectionInsets != null ) {
+ x += selectionInsets.left;
+ y += selectionInsets.top;
+ width -= selectionInsets.left + selectionInsets.right;
+ height -= selectionInsets.top + selectionInsets.bottom;
+ }
+ }
+
this.c = c;
super.paintBorder( c, g, x, y, width, height );
this.c = null;
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 a7b032eb3..c30d90bee 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
@@ -24,6 +24,9 @@
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
@@ -41,16 +44,23 @@
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JViewport;
+import javax.swing.ListSelectionModel;
import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.TableColumnModelEvent;
+import javax.swing.event.TableColumnModelListener;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTableUI;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumnModel;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
@@ -95,6 +105,8 @@
* @uiDefault Table.intercellSpacing Dimension
* @uiDefault Table.selectionInactiveBackground Color
* @uiDefault Table.selectionInactiveForeground Color
+ * @uiDefault Table.selectionInsets Insets
+ * @uiDefault Table.selectionArc int
* @uiDefault Table.paintOutsideAlternateRows boolean
* @uiDefault Table.editorSelectAllOnStartEditing boolean
*
@@ -123,6 +135,8 @@ public class FlatTableUI
@Styleable protected Color selectionForeground;
@Styleable protected Color selectionInactiveBackground;
@Styleable protected Color selectionInactiveForeground;
+ /** @since 3.5 */ @Styleable protected Insets selectionInsets;
+ /** @since 3.5 */ @Styleable protected int selectionArc;
// for FlatTableCellBorder
/** @since 2 */ @Styleable protected Insets cellMargins;
@@ -136,6 +150,8 @@ public class FlatTableUI
private PropertyChangeListener propertyChangeListener;
private ComponentListener outsideAlternateRowsListener;
+ private ListSelectionListener rowSelectionListener;
+ private TableColumnModelListener columnSelectionListener;
private Map
+ * Note: This method is only invoked if either selection arc
+ * is greater than zero or if selection insets are not empty.
+ *
+ * @since 3.5
+ */
+ protected void paintAlternateRowBackground( Graphics g, int row, int column, int x, int y, int width, int height ) {
+ Insets insets = (selectionInsets != null) ? (Insets) selectionInsets.clone() : null;
+ float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight;
+ arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f );
+
+ if( column >= 0 ) {
+ // selection insets
+
+ // selection arc
+ if( column > 0 ) {
+ if( insets != null )
+ insets.left = 0;
+
+ if( table.getComponentOrientation().isLeftToRight() )
+ arcTopLeft = arcBottomLeft = 0;
+ else
+ arcTopRight = arcBottomRight = 0;
+ }
+ if( column < table.getColumnCount() - 1 ) {
+ if( insets != null )
+ insets.right = 0;
+
+ if( table.getComponentOrientation().isLeftToRight() )
+ arcTopRight = arcBottomRight = 0;
+ else
+ arcTopLeft = arcBottomLeft = 0;
+ }
+ }
+
+ FlatUIUtils.paintSelection( (Graphics2D) g, x, y, width, height,
+ UIScale.scale( insets ), arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight, 0 );
+ }
+
+ /**
+ * Paints (rounded) cell selection.
+ * Supports {@link #selectionArc} and {@link #selectionInsets}.
+ *
+ * Note: This method is only invoked if either selection arc
+ * is greater than zero or if selection insets are not empty.
+ *
+ * @since 3.5
+ */
+ protected void paintCellSelection( Graphics g, int row, int column, int x, int y, int width, int height ) {
+ boolean rowSelAllowed = table.getRowSelectionAllowed();
+ boolean colSelAllowed = table.getColumnSelectionAllowed();
+ boolean rowSelOnly = rowSelAllowed && !colSelAllowed;
+ boolean colSelOnly = colSelAllowed && !rowSelAllowed;
+ boolean cellOnlySel = rowSelAllowed && colSelAllowed;
+
+ // get selection state of surrounding cells
+ boolean leftSelected = (column > 0 && (rowSelOnly || table.isCellSelected( row, column - 1 )));
+ boolean topSelected = (row > 0 && (colSelOnly || table.isCellSelected( row - 1, column )));
+ boolean rightSelected = (column < table.getColumnCount() - 1 && (rowSelOnly || table.isCellSelected( row, column + 1 )));
+ boolean bottomSelected = (row < table.getRowCount() - 1 && (colSelOnly || table.isCellSelected( row + 1, column )));
+ if( !table.getComponentOrientation().isLeftToRight() ) {
+ boolean temp = leftSelected;
+ leftSelected = rightSelected;
+ rightSelected = temp;
+ }
+
+ // selection insets
+ // (insets are applied to whole row if row-only selection is used,
+ // or to whole column if column-only selection is used,
+ // or to cell if cell selection is used)
+ Insets insets = (selectionInsets != null) ? (Insets) selectionInsets.clone() : null;
+ if( insets != null ) {
+ if( rowSelOnly && leftSelected )
+ insets.left = 0;
+ if( rowSelOnly && rightSelected )
+ insets.right = 0;
+ if( colSelOnly && topSelected )
+ insets.top = 0;
+ if( colSelOnly && bottomSelected )
+ insets.bottom = 0;
+ }
+
+ // selection arc
+ float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight;
+ arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f );
+ if( selectionArc > 0 ) {
+ // note that intercellSpacing is not considered as a gap because
+ // grid lines are usually painted to intercell space
+ boolean hasRowGap = (rowSelOnly || cellOnlySel) && insets != null && (insets.top != 0 || insets.bottom != 0);
+ boolean hasColGap = (colSelOnly || cellOnlySel) && insets != null && (insets.left != 0 || insets.right != 0);
+
+ if( leftSelected && !hasColGap )
+ arcTopLeft = arcBottomLeft = 0;
+ if( rightSelected && !hasColGap )
+ arcTopRight = arcBottomRight = 0;
+ if( topSelected && !hasRowGap )
+ arcTopLeft = arcTopRight = 0;
+ if( bottomSelected && !hasRowGap )
+ arcBottomLeft = arcBottomRight = 0;
+ }
+
+ FlatUIUtils.paintSelection( (Graphics2D) g, x, y, width, height,
+ UIScale.scale( insets ), arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight, 0 );
+ }
+
+ /**
+ * Paints a cell selection at the given coordinates.
+ * The selection color must be set on the graphics context.
+ *
+ * This method is intended for use in custom cell renderers to support
+ * {@link #selectionArc} and {@link #selectionInsets}.
+ *
+ * @since 3.5
+ */
+ public static void paintCellSelection( JTable table, Graphics g, int row, int column, int x, int y, int width, int height ) {
+ if( !(table.getUI() instanceof FlatTableUI) )
+ return;
+
+ FlatTableUI ui = (FlatTableUI) table.getUI();
+ ui.paintCellSelection( g, row, column, x, y, width, height );
+ }
+
+ private void installRepaintRoundedSelectionListeners() {
+ if( rowSelectionListener == null ) {
+ rowSelectionListener = this::repaintRoundedRowSelection;
+ table.getSelectionModel().addListSelectionListener( rowSelectionListener );
+ }
+
+ if( columnSelectionListener == null ) {
+ columnSelectionListener = new TableColumnModelListener() {
+ @Override
+ public void columnSelectionChanged( ListSelectionEvent e ) {
+ repaintRoundedColumnSelection( e );
+ }
+ @Override public void columnRemoved( TableColumnModelEvent e ) {}
+ @Override public void columnMoved( TableColumnModelEvent e ) {}
+ @Override public void columnMarginChanged( ChangeEvent e ) {}
+ @Override public void columnAdded( TableColumnModelEvent e ) {}
+ };
+ table.getColumnModel().addColumnModelListener( columnSelectionListener );
+ }
+ }
+
+ private void repaintRoundedRowSelection( ListSelectionEvent e ) {
+ if( selectionArc <= 0 || !table.getRowSelectionAllowed() )
+ return;
+
+ int rowCount = table.getRowCount();
+ int columnCount = table.getColumnCount();
+ if( rowCount <= 0 || columnCount <= 0 )
+ return;
+
+ // repaint including rows before and after changed selection
+ int firstRow = Math.max( 0, Math.min( e.getFirstIndex() - 1, rowCount - 1 ) );
+ int lastRow = Math.max( 0, Math.min( e.getLastIndex() + 1, rowCount - 1 ) );
+ Rectangle firstRect = table.getCellRect( firstRow, 0, false );
+ Rectangle lastRect = table.getCellRect( lastRow, columnCount - 1, false );
+ table.repaint( firstRect.union( lastRect ) );
+ }
+
+ private void repaintRoundedColumnSelection( ListSelectionEvent e ) {
+ if( selectionArc <= 0 || !table.getColumnSelectionAllowed() )
+ return;
+
+ int rowCount = table.getRowCount();
+ int columnCount = table.getColumnCount();
+ if( rowCount <= 0 || columnCount <= 0 )
+ return;
+
+ // limit to selected rows for cell selection
+ int firstRow = 0;
+ int lastRow = rowCount - 1;
+ if( table.getRowSelectionAllowed() ) {
+ firstRow = table.getSelectionModel().getMinSelectionIndex();
+ lastRow = table.getSelectionModel().getMaxSelectionIndex();
+ }
+
+ // repaint including columns before and after changed selection
+ int firstColumn = Math.max( 0, Math.min( e.getFirstIndex() - 1, columnCount - 1 ) );
+ int lastColumn = Math.max( 0, Math.min( e.getLastIndex() + 1, columnCount - 1 ) );
+ Rectangle firstRect = table.getCellRect( firstRow, firstColumn, false );
+ Rectangle lastRect = table.getCellRect( lastRow, lastColumn, false );
+ table.repaint( firstRect.union( lastRect ) );
+ }
+
+ //---- class RoundedSelectionGraphics -------------------------------------
+
+ /**
+ * Because selection painting is done in the cell renderer, it would be
+ * necessary to require a FlatLaf specific renderer to implement rounded selection.
+ * Using a LaF specific renderer was avoided because often a custom renderer is
+ * already used in applications. Then either the rounded selection is not used,
+ * or the application has to be changed to extend a FlatLaf renderer.
+ *
+ * To solve this, a graphics proxy is used that paints rounded selection
+ * if row/column/cell is selected and the renderer wants to fill the background.
+ */
+ private class RoundedSelectionGraphics
+ extends Graphics2DProxy
+ {
+ private final Color alternateRowColor;
+
+ // used to avoid endless loop in case that paintCellSelection() invokes
+ // g.fillRect() with full bounds (selectionInsets is 0,0,0,0)
+ private boolean inPaintSelection;
+
+ RoundedSelectionGraphics( Graphics delegate, Color alternateRowColor ) {
+ super( (Graphics2D) delegate );
+ this.alternateRowColor = alternateRowColor;
+ }
+
+ @Override
+ public Graphics create() {
+ return new RoundedSelectionGraphics( super.create(), alternateRowColor );
+ }
+
+ @Override
+ public Graphics create( int x, int y, int width, int height ) {
+ return new RoundedSelectionGraphics( super.create( x, y, width, height ), alternateRowColor );
+ }
+
+ @Override
+ public void fillRect( int x, int y, int width, int height ) {
+ if( fillCellSelection( x, y, width, height ) )
+ return;
+
+ super.fillRect( x, y, width, height );
+ }
+
+ @Override
+ public void fill( Shape shape ) {
+ if( shape instanceof Rectangle2D ) {
+ Rectangle2D r = (Rectangle2D) shape;
+ double x = r.getX();
+ double y = r.getY();
+ double width = r.getWidth();
+ double height = r.getHeight();
+ if( x == (int) x && y == (int) y && width == (int) width && height == (int) height ) {
+ if( fillCellSelection( (int) x, (int) y, (int) width, (int) height ) )
+ return;
+ }
+ }
+
+ super.fill( shape );
+ }
+
+ private boolean fillCellSelection( int x, int y, int width, int height ) {
+ if( inPaintSelection )
+ return false;
+
+ Color color;
+ Component rendererComponent;
+ if( x == 0 && y == 0 &&
+ ((color = getColor()) == table.getSelectionBackground() ||
+ (alternateRowColor != null && color == alternateRowColor)) &&
+ (rendererComponent = findActiveRendererComponent()) != null &&
+ width == rendererComponent.getWidth() &&
+ height == rendererComponent.getHeight() )
+ {
+ Point location = rendererComponent.getLocation();
+ int row = table.rowAtPoint( location );
+ int column = table.columnAtPoint( location );
+ if( row >= 0 && column >= 0 ) {
+ inPaintSelection = true;
+ if( color == table.getSelectionBackground() )
+ paintCellSelection( this, row, column, x, y, width, height );
+ else
+ paintAlternateRowBackground( this, row, column, x, y, width, height );
+ inPaintSelection = false;
+
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * A CellRendererPane may contain multiple components, if multiple renderers
+ * are used. Inactive renderer components have size {@code 0x0}.
+ */
+ private Component findActiveRendererComponent() {
+ int count = rendererPane.getComponentCount();
+ for( int i = 0; i < count; i++ ) {
+ Component c = rendererPane.getComponent( i );
+ if( c.getWidth() > 0 && c.getHeight() > 0 )
+ return c;
+ }
+ return null;
+ }
+ }
+
//---- class OutsideAlternateRowsListener ---------------------------------
/**
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java
index 115185cae..011ac2e69 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java
@@ -124,6 +124,11 @@ public static void setInsets( Insets dest, Insets src ) {
dest.right = src.right;
}
+ /** @since 3.5 */
+ public static boolean isInsetsEmpty( Insets insets ) {
+ return insets.top == 0 && insets.left == 0 && insets.bottom == 0 && insets.right == 0;
+ }
+
public static Color getUIColor( String key, int defaultColorRGB ) {
Color color = UIManager.getColor( key );
return (color != null) ? color : new Color( defaultColorRGB );
diff --git a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableInfo.java b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableInfo.java
index ade216816..ea536a16c 100644
--- a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableInfo.java
+++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableInfo.java
@@ -800,6 +800,8 @@ void table() {
"selectionForeground", Color.class,
"selectionInactiveBackground", Color.class,
"selectionInactiveForeground", Color.class,
+ "selectionInsets", Insets.class,
+ "selectionArc", int.class,
// FlatTableCellBorder
"cellMargins", Insets.class,
diff --git a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableValue.java b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableValue.java
index 2fd174aa5..ed4708fe4 100644
--- a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableValue.java
+++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableValue.java
@@ -305,6 +305,9 @@ void comboBox() {
testColor( c, ui, "buttonPressedArrowColor", 0x123456 );
testColor( c, ui, "popupBackground", 0x123456 );
+ testInsets( c, ui, "popupInsets", 1,2,3,4 );
+ testInsets( c, ui, "selectionInsets", 1,2,3,4 );
+ testInteger( c, ui, "selectionArc", 123 );
// border
flatRoundBorder( c, ui );
@@ -367,6 +370,8 @@ void list() {
testColor( c, ui, "selectionForeground", 0x123456 );
testColor( c, ui, "selectionInactiveBackground", 0x123456 );
testColor( c, ui, "selectionInactiveForeground", 0x123456 );
+ testInsets( c, ui, "selectionInsets", 1,2,3,4 );
+ testInteger( c, ui, "selectionArc", 123 );
// FlatListCellBorder
testInsets( c, ui, "cellMargins", 1,2,3,4 );
@@ -802,6 +807,8 @@ void table() {
testColor( c, ui, "selectionForeground", 0x123456 );
testColor( c, ui, "selectionInactiveBackground", 0x123456 );
testColor( c, ui, "selectionInactiveForeground", 0x901324 );
+ testInsets( c, ui, "selectionInsets", 1,2,3,4 );
+ testInteger( c, ui, "selectionArc", 123 );
// FlatTableCellBorder
testInsets( c, ui, "cellMargins", 1,2,3,4 );
@@ -931,6 +938,8 @@ void tree() {
testColor( c, ui, "selectionInactiveBackground", 0x123456 );
testColor( c, ui, "selectionInactiveForeground", 0x123456 );
testColor( c, ui, "selectionBorderColor", 0x123456 );
+ testInsets( c, ui, "selectionInsets", 1,2,3,4 );
+ testInteger( c, ui, "selectionArc", 123 );
testBoolean( c, ui, "wideSelection", true );
testBoolean( c, ui, "showCellFocusIndicator", true );
diff --git a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyling.java b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyling.java
index 6ac0c60fc..204cf8bee 100644
--- a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyling.java
+++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyling.java
@@ -987,6 +987,8 @@ void table() {
ui.applyStyle( "selectionForeground: #fff" );
ui.applyStyle( "selectionInactiveBackground: #fff" );
ui.applyStyle( "selectionInactiveForeground: #fff" );
+ ui.applyStyle( "selectionInsets: 1,2,3,4" );
+ ui.applyStyle( "selectionArc: 8" );
// FlatTableCellBorder
ui.applyStyle( "cellMargins: 1,2,3,4" );
diff --git a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DataComponentsPanel.java b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DataComponentsPanel.java
index 952f7e01a..e936658b1 100644
--- a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DataComponentsPanel.java
+++ b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DataComponentsPanel.java
@@ -25,6 +25,8 @@
import javax.swing.table.*;
import javax.swing.tree.*;
import com.formdev.flatlaf.FlatClientProperties;
+import com.formdev.flatlaf.FlatLaf;
+import com.formdev.flatlaf.util.ColorFunctions;
import net.miginfocom.swing.*;
/**
@@ -93,10 +95,12 @@ private void dndChanged() {
private void rowSelectionChanged() {
table1.setRowSelectionAllowed( rowSelectionCheckBox.isSelected() );
+ roundedSelectionChanged();
}
private void columnSelectionChanged() {
table1.setColumnSelectionAllowed( columnSelectionCheckBox.isSelected() );
+ roundedSelectionChanged();
}
private void showHorizontalLinesChanged() {
@@ -127,6 +131,28 @@ private void intercellSpacingPropertyChange() {
intercellSpacingCheckBox.setSelected( table1.getRowMargin() != 0 );
}
+ private void roundedSelectionChanged() {
+ String style = null;
+ if( roundedSelectionCheckBox.isSelected() ) {
+ style = rowSelectionCheckBox.isSelected()
+ ? "selectionArc: 6; selectionInsets: 0,1,0,1"
+ : "selectionArc: 6";
+ }
+ table1.putClientProperty( FlatClientProperties.STYLE, style );
+ }
+
+ private void alternatingRowsChanged() {
+ Color alternateRowColor = null;
+ if( alternatingRowsCheckBox.isSelected() ) {
+ Color background = table1.getBackground();
+ alternateRowColor = FlatLaf.isLafDark()
+ ? ColorFunctions.lighten( background, 0.05f )
+ : ColorFunctions.darken( background, 0.05f );
+ }
+ UIManager.put( "Table.alternateRowColor", alternateRowColor );
+ table1.repaint();
+ }
+
@SuppressWarnings( { "unchecked", "rawtypes" } )
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
@@ -151,12 +177,14 @@ private void initComponents() {
JScrollPane scrollPane5 = new JScrollPane();
table1 = new JTable();
JPanel tableOptionsPanel = new JPanel();
+ roundedSelectionCheckBox = new JCheckBox();
showHorizontalLinesCheckBox = new JCheckBox();
showVerticalLinesCheckBox = new JCheckBox();
intercellSpacingCheckBox = new JCheckBox();
redGridColorCheckBox = new JCheckBox();
rowSelectionCheckBox = new JCheckBox();
columnSelectionCheckBox = new JCheckBox();
+ alternatingRowsCheckBox = new JCheckBox();
dndCheckBox = new JCheckBox();
JPopupMenu popupMenu2 = new JPopupMenu();
JMenuItem menuItem3 = new JMenuItem();
@@ -403,44 +431,56 @@ public boolean isCellEditable(int rowIndex, int columnIndex) {
"[]0" +
"[]0" +
"[]0" +
+ "[]0" +
+ "[]0" +
"[]0"));
+ //---- roundedSelectionCheckBox ----
+ roundedSelectionCheckBox.setText("rounded selection");
+ roundedSelectionCheckBox.addActionListener(e -> roundedSelectionChanged());
+ tableOptionsPanel.add(roundedSelectionCheckBox, "cell 0 0");
+
//---- showHorizontalLinesCheckBox ----
showHorizontalLinesCheckBox.setText("show horizontal lines");
showHorizontalLinesCheckBox.addActionListener(e -> showHorizontalLinesChanged());
- tableOptionsPanel.add(showHorizontalLinesCheckBox, "cell 0 0");
+ tableOptionsPanel.add(showHorizontalLinesCheckBox, "cell 0 1");
//---- showVerticalLinesCheckBox ----
showVerticalLinesCheckBox.setText("show vertical lines");
showVerticalLinesCheckBox.addActionListener(e -> showVerticalLinesChanged());
- tableOptionsPanel.add(showVerticalLinesCheckBox, "cell 0 1");
+ tableOptionsPanel.add(showVerticalLinesCheckBox, "cell 0 2");
//---- intercellSpacingCheckBox ----
intercellSpacingCheckBox.setText("intercell spacing");
intercellSpacingCheckBox.addActionListener(e -> intercellSpacingChanged());
- tableOptionsPanel.add(intercellSpacingCheckBox, "cell 0 2");
+ tableOptionsPanel.add(intercellSpacingCheckBox, "cell 0 3");
//---- redGridColorCheckBox ----
redGridColorCheckBox.setText("red grid color");
redGridColorCheckBox.addActionListener(e -> redGridColorChanged());
- tableOptionsPanel.add(redGridColorCheckBox, "cell 0 3");
+ tableOptionsPanel.add(redGridColorCheckBox, "cell 0 4");
//---- rowSelectionCheckBox ----
rowSelectionCheckBox.setText("row selection");
rowSelectionCheckBox.setSelected(true);
rowSelectionCheckBox.addActionListener(e -> rowSelectionChanged());
- tableOptionsPanel.add(rowSelectionCheckBox, "cell 0 4");
+ tableOptionsPanel.add(rowSelectionCheckBox, "cell 0 5");
//---- columnSelectionCheckBox ----
columnSelectionCheckBox.setText("column selection");
columnSelectionCheckBox.addActionListener(e -> columnSelectionChanged());
- tableOptionsPanel.add(columnSelectionCheckBox, "cell 0 5");
+ tableOptionsPanel.add(columnSelectionCheckBox, "cell 0 6");
+
+ //---- alternatingRowsCheckBox ----
+ alternatingRowsCheckBox.setText("alternating rows");
+ alternatingRowsCheckBox.addActionListener(e -> alternatingRowsChanged());
+ tableOptionsPanel.add(alternatingRowsCheckBox, "cell 0 7");
//---- dndCheckBox ----
dndCheckBox.setText("enable drag and drop");
dndCheckBox.setMnemonic('D');
dndCheckBox.addActionListener(e -> dndChanged());
- tableOptionsPanel.add(dndCheckBox, "cell 0 6");
+ tableOptionsPanel.add(dndCheckBox, "cell 0 8");
}
add(tableOptionsPanel, "cell 4 3");
@@ -477,12 +517,14 @@ public boolean isCellEditable(int rowIndex, int columnIndex) {
private JTree tree3;
private JTree tree2;
private JTable table1;
+ private JCheckBox roundedSelectionCheckBox;
private JCheckBox showHorizontalLinesCheckBox;
private JCheckBox showVerticalLinesCheckBox;
private JCheckBox intercellSpacingCheckBox;
private JCheckBox redGridColorCheckBox;
private JCheckBox rowSelectionCheckBox;
private JCheckBox columnSelectionCheckBox;
+ private JCheckBox alternatingRowsCheckBox;
private JCheckBox dndCheckBox;
// JFormDesigner - End of variables declaration //GEN-END:variables
diff --git a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DataComponentsPanel.jfd b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DataComponentsPanel.jfd
index bde3e024a..0f729bb18 100644
--- a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DataComponentsPanel.jfd
+++ b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DataComponentsPanel.jfd
@@ -343,9 +343,19 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets 0,hidemode 3"
"$columnConstraints": "[]"
- "$rowConstraints": "[]0[]0[]0[]0[]0[]0[]0"
+ "$rowConstraints": "[]0[]0[]0[]0[]0[]0[]0[]0[]0"
} ) {
name: "tableOptionsPanel"
+ add( new FormComponent( "javax.swing.JCheckBox" ) {
+ name: "roundedSelectionCheckBox"
+ "text": "rounded selection"
+ auxiliary() {
+ "JavaCodeGenerator.variableLocal": false
+ }
+ addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "roundedSelectionChanged", false ) )
+ }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
+ "value": "cell 0 0"
+ } )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showHorizontalLinesCheckBox"
"text": "show horizontal lines"
@@ -354,7 +364,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showHorizontalLinesChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 0 0"
+ "value": "cell 0 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showVerticalLinesCheckBox"
@@ -364,7 +374,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showVerticalLinesChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 0 1"
+ "value": "cell 0 2"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "intercellSpacingCheckBox"
@@ -374,7 +384,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "intercellSpacingChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 0 2"
+ "value": "cell 0 3"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "redGridColorCheckBox"
@@ -384,7 +394,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "redGridColorChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 0 3"
+ "value": "cell 0 4"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "rowSelectionCheckBox"
@@ -395,7 +405,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "rowSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 0 4"
+ "value": "cell 0 5"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "columnSelectionCheckBox"
@@ -405,7 +415,17 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "columnSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 0 5"
+ "value": "cell 0 6"
+ } )
+ 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 0 7"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "dndCheckBox"
@@ -416,7 +436,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "dndChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 0 6"
+ "value": "cell 0 8"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 3"
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 a21f27e4b..57cc183b3 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
@@ -47,6 +47,7 @@
import com.formdev.flatlaf.icons.FlatMenuArrowIcon;
import com.formdev.flatlaf.ui.FlatEmptyBorder;
import com.formdev.flatlaf.ui.FlatListUI;
+import com.formdev.flatlaf.ui.FlatTableUI;
import com.formdev.flatlaf.util.UIScale;
import com.jidesoft.swing.*;
import com.jidesoft.swing.CheckBoxTreeCellRenderer;
@@ -131,6 +132,14 @@ public static void main( String[] args ) {
initTableEditors( table1 );
initTableEditors( xTable1 );
+ // table selection listeners
+ table1.getSelectionModel().addListSelectionListener( e -> {
+ System.out.printf( "row sel %d-%d adj=%b\n", e.getFirstIndex(), e.getLastIndex(), e.getValueIsAdjusting() );
+ } );
+ table1.getColumnModel().getSelectionModel().addListSelectionListener( e -> {
+ System.out.printf( "column sel %d-%d adj=%b\n", e.getFirstIndex(), e.getLastIndex(), e.getValueIsAdjusting() );
+ } );
+
// JXTable
Highlighter simpleStriping = HighlighterFactory.createSimpleStriping();
PatternPredicate patternPredicate = new PatternPredicate( "^J", 2 );
@@ -164,6 +173,8 @@ private void initTableEditors( JTable table ) {
JComboBox