diff --git a/CHANGELOG.md b/CHANGELOG.md index 45530980..90e09809 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ FlatLaf Change Log - If frame is deiconified, dock is compacted (icons move to the left). - If dock is wider than desktop width, additional rows are used. - If desktop pane is resized, layout of dock is updated. +- TableHeader: Moved table header column border painting from + `FlatTableHeaderUI` to new border `FlatTableHeaderBorder` to improve + compatibility with custom table header implementations. (issue #228) - IntelliJ Themes: Added "Material Theme UI Lite / GitHub Dark" theme. - JIDE Common Layer: Improved support for `JideTabbedPane`. (PR #306) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableHeaderBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableHeaderBorder.java new file mode 100644 index 00000000..d8e6e87c --- /dev/null +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableHeaderBorder.java @@ -0,0 +1,124 @@ +/* + * Copyright 2021 FormDev Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.formdev.flatlaf.ui; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; +import javax.swing.JScrollBar; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableColumn; +import com.formdev.flatlaf.util.UIScale; + +/** + * Cell border for {@code sun.swing.table.DefaultTableCellHeaderRenderer} + * (used by {@link javax.swing.table.JTableHeader}). + *
+ * Uses separate cell margins from UI defaults to allow easy customizing.
+ *
+ * @author Karl Tauber
+ * @since 1.2
+ */
+public class FlatTableHeaderBorder
+ extends FlatEmptyBorder
+{
+ protected Color separatorColor = UIManager.getColor( "TableHeader.separatorColor" );
+ protected Color bottomSeparatorColor = UIManager.getColor( "TableHeader.bottomSeparatorColor" );
+
+ public FlatTableHeaderBorder() {
+ super( UIManager.getInsets( "TableHeader.cellMargins" ) );
+ }
+
+ @Override
+ public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
+ JTableHeader header = (JTableHeader) SwingUtilities.getAncestorOfClass( JTableHeader.class, c );
+ boolean leftToRight = (header != null ? header : c).getComponentOrientation().isLeftToRight();
+ boolean paintLeft = !leftToRight;
+ boolean paintRight = leftToRight;
+
+ if( header != null ) {
+ int hx = SwingUtilities.convertPoint( c, x, y, header ).x;
+ if( isDraggedColumn( header, hx ) )
+ paintLeft = paintRight = true;
+ else {
+ if( hx <= 0 && !leftToRight && hideTrailingVerticalLine( header ) )
+ paintLeft = false;
+ if( hx + width >= header.getWidth() && leftToRight && hideTrailingVerticalLine( header ) )
+ paintRight = false;
+ }
+ }
+
+ float lineWidth = UIScale.scale( 1f );
+
+ Graphics2D g2 = (Graphics2D) g.create();
+ try {
+ FlatUIUtils.setRenderingHints( g2 );
+
+ // paint column separator lines
+ g2.setColor( separatorColor );
+ if( paintLeft )
+ g2.fill( new Rectangle2D.Float( x, y, lineWidth, height - lineWidth ) );
+ if( paintRight )
+ g2.fill( new Rectangle2D.Float( x + width - lineWidth, y, lineWidth, height - lineWidth ) );
+
+ // paint bottom line
+ g2.setColor( bottomSeparatorColor );
+ g2.fill( new Rectangle2D.Float( x, y + height - lineWidth, width, lineWidth ) );
+ } finally {
+ g2.dispose();
+ }
+ }
+
+ protected boolean isDraggedColumn( JTableHeader header, int x ) {
+ TableColumn draggedColumn = header.getDraggedColumn();
+ if( draggedColumn == null )
+ return false;
+
+ int draggedDistance = header.getDraggedDistance();
+ if( draggedDistance == 0 )
+ return false;
+
+ int columnCount = header.getColumnModel().getColumnCount();
+ for( int i = 0; i < columnCount; i++ ) {
+ if( header.getHeaderRect( i ).x + draggedDistance == x )
+ return true;
+ }
+
+ return false;
+ }
+
+ protected boolean hideTrailingVerticalLine( JTableHeader header ) {
+ Container viewport = header.getParent();
+ Container viewportParent = (viewport != null) ? viewport.getParent() : null;
+ if( !(viewportParent instanceof JScrollPane) )
+ return true;
+
+ JScrollBar vsb = ((JScrollPane)viewportParent).getVerticalScrollBar();
+ if( vsb == null || !vsb.isVisible() )
+ return true;
+
+ // if "ScrollPane.fillUpperCorner" is true, then javax.swing.ScrollPaneLayout
+ // extends the vertical scrollbar into the upper right/left corner
+ return vsb.getY() == viewport.getY();
+ }
+}
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableHeaderUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableHeaderUI.java
index a42d5681..f19b5202 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableHeaderUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableHeaderUI.java
@@ -18,20 +18,16 @@ package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Component;
-import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
-import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.util.Objects;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
-import javax.swing.JScrollPane;
import javax.swing.JTable;
-import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.Border;
@@ -39,7 +35,6 @@ import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTableHeaderUI;
import javax.swing.table.TableCellRenderer;
-import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import com.formdev.flatlaf.util.UIScale;
@@ -54,17 +49,21 @@ import com.formdev.flatlaf.util.UIScale;
*
*
*
- * @uiDefault TableHeader.separatorColor Color
* @uiDefault TableHeader.bottomSeparatorColor Color
* @uiDefault TableHeader.height int
* @uiDefault TableHeader.sortIconPosition String right (default), left, top or bottom
*
+ *
+ *
+ * @uiDefault TableHeader.cellMargins Insets
+ * @uiDefault TableHeader.separatorColor Color
+ * @uiDefault TableHeader.bottomSeparatorColor Color
+ *
* @author Karl Tauber
*/
public class FlatTableHeaderUI
extends BasicTableHeaderUI
{
- protected Color separatorColor;
protected Color bottomSeparatorColor;
protected int height;
protected int sortIconPosition;
@@ -77,7 +76,6 @@ public class FlatTableHeaderUI
protected void installDefaults() {
super.installDefaults();
- separatorColor = UIManager.getColor( "TableHeader.separatorColor" );
bottomSeparatorColor = UIManager.getColor( "TableHeader.bottomSeparatorColor" );
height = UIManager.getInt( "TableHeader.height" );
switch( Objects.toString( UIManager.getString( "TableHeader.sortIconPosition" ), "right" ) ) {
@@ -93,27 +91,38 @@ public class FlatTableHeaderUI
protected void uninstallDefaults() {
super.uninstallDefaults();
- separatorColor = null;
bottomSeparatorColor = null;
}
@Override
public void paint( Graphics g, JComponent c ) {
- if( header.getColumnModel().getColumnCount() <= 0 )
+ TableColumnModel columnModel = header.getColumnModel();
+ if( columnModel.getColumnCount() <= 0 )
return;
- // do not paint borders if JTableHeader.setDefaultRenderer() was used
- TableCellRenderer defaultRenderer = header.getDefaultRenderer();
- boolean paintBorders = isSystemDefaultRenderer( defaultRenderer );
- if( !paintBorders ) {
- // check whether the renderer delegates to the system default renderer
- Component rendererComponent = defaultRenderer.getTableCellRendererComponent(
- header.getTable(), "", false, false, -1, 0 );
- paintBorders = isSystemDefaultRenderer( rendererComponent );
- }
+ // compute total width of all columns
+ int columnCount = columnModel.getColumnCount();
+ int totalWidth = 0;
+ for( int i = 0; i < columnCount; i++ )
+ totalWidth += columnModel.getColumn( i ).getWidth();
- if( paintBorders )
- paintColumnBorders( g, c );
+ if( totalWidth < header.getWidth() ) {
+ // do not paint bottom separator if JTableHeader.setDefaultRenderer() was used
+ TableCellRenderer defaultRenderer = header.getDefaultRenderer();
+ boolean paintBottomSeparator = isSystemDefaultRenderer( defaultRenderer );
+ if( !paintBottomSeparator && header.getTable() != null ) {
+ // check whether the renderer delegates to the system default renderer
+ Component rendererComponent = defaultRenderer.getTableCellRendererComponent(
+ header.getTable(), "", false, false, -1, 0 );
+ paintBottomSeparator = isSystemDefaultRenderer( rendererComponent );
+ }
+
+ if( paintBottomSeparator ) {
+ int w = c.getWidth() - totalWidth;
+ int x = header.getComponentOrientation().isLeftToRight() ? c.getWidth() - w : 0;
+ paintBottomSeparator( g, c, x, w );
+ }
+ }
// temporary use own default renderer if necessary
FlatTableCellHeaderRenderer sortIconRenderer = null;
@@ -130,9 +139,6 @@ public class FlatTableHeaderUI
sortIconRenderer.reset();
header.setDefaultRenderer( sortIconRenderer.delegate );
}
-
- if( paintBorders )
- paintDraggedColumnBorders( g, c );
}
private boolean isSystemDefaultRenderer( Object headerRenderer ) {
@@ -141,17 +147,8 @@ public class FlatTableHeaderUI
rendererClassName.equals( "sun.swing.FilePane$AlignableTableHeaderRenderer" );
}
- protected void paintColumnBorders( Graphics g, JComponent c ) {
- int width = c.getWidth();
- int height = c.getHeight();
+ protected void paintBottomSeparator( Graphics g, JComponent c, int x, int w ) {
float lineWidth = UIScale.scale( 1f );
- float topLineIndent = lineWidth;
- float bottomLineIndent = lineWidth * 3;
- TableColumnModel columnModel = header.getColumnModel();
- int columnCount = columnModel.getColumnCount();
- int sepCount = columnCount;
- if( hideLastVerticalLine() )
- sepCount--;
Graphics2D g2 = (Graphics2D) g.create();
try {
@@ -159,78 +156,7 @@ public class FlatTableHeaderUI
// paint bottom line
g2.setColor( bottomSeparatorColor );
- g2.fill( new Rectangle2D.Float( 0, height - lineWidth, width, lineWidth ) );
-
- // paint column separator lines
- g2.setColor( separatorColor );
-
- float y = topLineIndent;
- float h = height - bottomLineIndent;
-
- if( header.getComponentOrientation().isLeftToRight() ) {
- int x = 0;
- for( int i = 0; i < sepCount; i++ ) {
- x += columnModel.getColumn( i ).getWidth();
- g2.fill( new Rectangle2D.Float( x - lineWidth, y, lineWidth, h ) );
- }
-
- // paint trailing separator (on right side)
- if( !hideTrailingVerticalLine() )
- g2.fill( new Rectangle2D.Float( header.getWidth() - lineWidth, y, lineWidth, h ) );
- } else {
- Rectangle cellRect = header.getHeaderRect( 0 );
- int x = cellRect.x + cellRect.width;
- for( int i = 0; i < sepCount; i++ ) {
- x -= columnModel.getColumn( i ).getWidth();
- g2.fill( new Rectangle2D.Float( x - (i < sepCount - 1 ? lineWidth : 0), y, lineWidth, h ) );
- }
-
- // paint trailing separator (on left side)
- if( !hideTrailingVerticalLine() )
- g2.fill( new Rectangle2D.Float( 0, y, lineWidth, h ) );
- }
- } finally {
- g2.dispose();
- }
- }
-
- private void paintDraggedColumnBorders( Graphics g, JComponent c ) {
- TableColumn draggedColumn = header.getDraggedColumn();
- if( draggedColumn == null )
- return;
-
- // find index of dragged column
- TableColumnModel columnModel = header.getColumnModel();
- int columnCount = columnModel.getColumnCount();
- int draggedColumnIndex = -1;
- for( int i = 0; i < columnCount; i++ ) {
- if( columnModel.getColumn( i ) == draggedColumn ) {
- draggedColumnIndex = i;
- break;
- }
- }
-
- if( draggedColumnIndex < 0 )
- return;
-
- float lineWidth = UIScale.scale( 1f );
- float topLineIndent = lineWidth;
- float bottomLineIndent = lineWidth * 3;
- Rectangle r = header.getHeaderRect( draggedColumnIndex );
- r.x += header.getDraggedDistance();
-
- Graphics2D g2 = (Graphics2D) g.create();
- try {
- FlatUIUtils.setRenderingHints( g2 );
-
- // paint dragged bottom line
- g2.setColor( bottomSeparatorColor );
- g2.fill( new Rectangle2D.Float( r.x, r.y + r.height - lineWidth, r.width, lineWidth ) );
-
- // paint dragged column separator lines
- g2.setColor( separatorColor );
- g2.fill( new Rectangle2D.Float( r.x, topLineIndent, lineWidth, r.height - bottomLineIndent ) );
- g2.fill( new Rectangle2D.Float( r.x + r.width - lineWidth, r.y + topLineIndent, lineWidth, r.height - bottomLineIndent ) );
+ g2.fill( new Rectangle2D.Float( x, c.getHeight() - lineWidth, w, lineWidth ) );
} finally {
g2.dispose();
}
@@ -244,32 +170,6 @@ public class FlatTableHeaderUI
return size;
}
- protected boolean hideLastVerticalLine() {
- Container viewport = header.getParent();
- Container viewportParent = (viewport != null) ? viewport.getParent() : null;
- if( !(viewportParent instanceof JScrollPane) )
- return false;
-
- Rectangle cellRect = header.getHeaderRect( header.getColumnModel().getColumnCount() - 1 );
-
- // using component orientation of scroll pane here because it is also used in FlatTableUI
- JScrollPane scrollPane = (JScrollPane) viewportParent;
- return scrollPane.getComponentOrientation().isLeftToRight()
- ? cellRect.x + cellRect.width >= viewport.getWidth()
- : cellRect.x <= 0;
- }
-
- protected boolean hideTrailingVerticalLine() {
- Container viewport = header.getParent();
- Container viewportParent = (viewport != null) ? viewport.getParent() : null;
- if( !(viewportParent instanceof JScrollPane) )
- return false;
-
- JScrollPane scrollPane = (JScrollPane) viewportParent;
- return viewport == scrollPane.getColumnHeader() &&
- scrollPane.getCorner( ScrollPaneConstants.UPPER_TRAILING_CORNER ) == null;
- }
-
//---- class FlatTableCellHeaderRenderer ----------------------------------
/**
diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties
index 87949600..52e8bff9 100644
--- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties
+++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties
@@ -642,7 +642,8 @@ Table.dropLineShortColor = @dropLineShortColor
#---- TableHeader ----
TableHeader.height = 25
-TableHeader.cellBorder = 2,3,2,3
+TableHeader.cellBorder = com.formdev.flatlaf.ui.FlatTableHeaderBorder
+TableHeader.cellMargins = 2,3,2,3
TableHeader.focusCellBackground = $TableHeader.background
TableHeader.background = @textComponentBackground
diff --git a/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0_202.txt b/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0_202.txt
index 03859b66..69102d69 100644
--- a/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0_202.txt
+++ b/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0_202.txt
@@ -1067,7 +1067,8 @@ Table.sortIconColor #adadad javax.swing.plaf.ColorUIResource [UI]
TableHeader.background #45494a javax.swing.plaf.ColorUIResource [UI]
TableHeader.bottomSeparatorColor #5e6364 javax.swing.plaf.ColorUIResource [UI]
-TableHeader.cellBorder [lazy] 2,3,2,3 false com.formdev.flatlaf.ui.FlatEmptyBorder [UI]
+TableHeader.cellBorder [lazy] 2,3,2,3 false com.formdev.flatlaf.ui.FlatTableHeaderBorder [UI]
+TableHeader.cellMargins 2,3,2,3 javax.swing.plaf.InsetsUIResource [UI]
TableHeader.focusCellBackground #45494a javax.swing.plaf.ColorUIResource [UI]
TableHeader.font [active] $defaultFont [UI]
TableHeader.foreground #bbbbbb javax.swing.plaf.ColorUIResource [UI]
diff --git a/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0_202.txt b/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0_202.txt
index d453bf72..272cc8a7 100644
--- a/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0_202.txt
+++ b/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0_202.txt
@@ -1072,7 +1072,8 @@ Table.sortIconColor #afafaf javax.swing.plaf.ColorUIResource [UI]
TableHeader.background #ffffff javax.swing.plaf.ColorUIResource [UI]
TableHeader.bottomSeparatorColor #e6e6e6 javax.swing.plaf.ColorUIResource [UI]
-TableHeader.cellBorder [lazy] 2,3,2,3 false com.formdev.flatlaf.ui.FlatEmptyBorder [UI]
+TableHeader.cellBorder [lazy] 2,3,2,3 false com.formdev.flatlaf.ui.FlatTableHeaderBorder [UI]
+TableHeader.cellMargins 2,3,2,3 javax.swing.plaf.InsetsUIResource [UI]
TableHeader.focusCellBackground #ffffff javax.swing.plaf.ColorUIResource [UI]
TableHeader.font [active] $defaultFont [UI]
TableHeader.foreground #000000 javax.swing.plaf.ColorUIResource [UI]
diff --git a/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0_202.txt b/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0_202.txt
index 42e64115..0ab6c35f 100644
--- a/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0_202.txt
+++ b/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0_202.txt
@@ -1070,7 +1070,8 @@ Table.sortIconColor #ffff00 javax.swing.plaf.ColorUIResource [UI]
TableHeader.background #4444ff javax.swing.plaf.ColorUIResource [UI]
TableHeader.bottomSeparatorColor #00ff00 javax.swing.plaf.ColorUIResource [UI]
-TableHeader.cellBorder [lazy] 2,3,2,3 false com.formdev.flatlaf.ui.FlatEmptyBorder [UI]
+TableHeader.cellBorder [lazy] 2,3,2,3 false com.formdev.flatlaf.ui.FlatTableHeaderBorder [UI]
+TableHeader.cellMargins 2,3,2,3 javax.swing.plaf.InsetsUIResource [UI]
TableHeader.focusCellBackground #4444ff javax.swing.plaf.ColorUIResource [UI]
TableHeader.font [active] $defaultFont [UI]
TableHeader.foreground #ffffff javax.swing.plaf.ColorUIResource [UI]
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 af2d3def..efa95f0b 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
@@ -40,6 +40,7 @@ import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellRenderer;
import com.formdev.flatlaf.FlatClientProperties;
+import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.icons.FlatMenuArrowIcon;
import com.formdev.flatlaf.util.UIScale;
import com.jidesoft.swing.*;
@@ -193,6 +194,15 @@ public class FlatComponents2Test
table.setAutoResizeMode( autoResizeMode );
}
+ private void sortIconPositionChanged() {
+ Object sel = sortIconPositionComboBox.getSelectedItem();
+ if( "right".equals( sel ) )
+ sel = null;
+
+ UIManager.put( "TableHeader.sortIconPosition", sel );
+ FlatLaf.updateUILater();
+ }
+
private void dndChanged() {
boolean dnd = dndCheckBox.isSelected();
list1.setDragEnabled( dnd );
@@ -442,6 +452,8 @@ public class FlatComponents2Test
JPanel tableOptionsPanel = new JPanel();
JLabel autoResizeModeLabel = new JLabel();
autoResizeModeField = new JComboBox<>();
+ JLabel sortIconPositionLabel = new JLabel();
+ sortIconPositionComboBox = new JComboBox<>();
showHorizontalLinesCheckBox = new JCheckBox();
rowSelectionCheckBox = new JCheckBox();
showVerticalLinesCheckBox = new JCheckBox();
@@ -753,6 +765,20 @@ public class FlatComponents2Test
autoResizeModeField.addActionListener(e -> autoResizeModeChanged());
tableOptionsPanel.add(autoResizeModeField, "cell 0 0 2 1");
+ //---- sortIconPositionLabel ----
+ sortIconPositionLabel.setText("Sort icon:");
+ tableOptionsPanel.add(sortIconPositionLabel, "cell 0 0 2 1");
+
+ //---- sortIconPositionComboBox ----
+ sortIconPositionComboBox.setModel(new DefaultComboBoxModel<>(new String[] {
+ "right",
+ "left",
+ "top",
+ "bottom"
+ }));
+ sortIconPositionComboBox.addActionListener(e -> sortIconPositionChanged());
+ tableOptionsPanel.add(sortIconPositionComboBox, "cell 0 0 2 1");
+
//---- showHorizontalLinesCheckBox ----
showHorizontalLinesCheckBox.setText("show horizontal lines");
showHorizontalLinesCheckBox.addActionListener(e -> showHorizontalLinesChanged());
@@ -819,6 +845,7 @@ public class FlatComponents2Test
private JCheckBox treeWideSelectionCheckBox;
private JCheckBox treePaintSelectionCheckBox;
private JComboBox