mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-06 22:10:54 +03:00
TableHeader: no longer temporary replace header cell renderer while painting (issue https://github.com/apache/netbeans/issues/6835)
This commit is contained in:
@@ -26,6 +26,10 @@ FlatLaf Change Log
|
|||||||
component is a Table.
|
component is a Table.
|
||||||
- Table: Fixed background of `boolean` columns when using alternating row
|
- Table: Fixed background of `boolean` columns when using alternating row
|
||||||
colors. (issue #780)
|
colors. (issue #780)
|
||||||
|
- TableHeader: No longer temporary replace header cell renderer while painting.
|
||||||
|
This avoids a `StackOverflowError` in case that custom renderer does this too.
|
||||||
|
(see [NetBeans issue #6835](https://github.com/apache/netbeans/issues/6835)).
|
||||||
|
This also improves compatibility with custom table header implementations.
|
||||||
- TabbedPane:
|
- TabbedPane:
|
||||||
- Avoid unnecessary repainting whole tabbed pane content area when layouting
|
- Avoid unnecessary repainting whole tabbed pane content area when layouting
|
||||||
leading/trailing components.
|
leading/trailing components.
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
import java.awt.Cursor;
|
import java.awt.Cursor;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
@@ -28,16 +29,15 @@ import java.awt.event.MouseEvent;
|
|||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import javax.swing.CellRendererPane;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.border.Border;
|
|
||||||
import javax.swing.event.MouseInputListener;
|
import javax.swing.event.MouseInputListener;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
|
||||||
import javax.swing.plaf.basic.BasicTableHeaderUI;
|
import javax.swing.plaf.basic.BasicTableHeaderUI;
|
||||||
import javax.swing.table.JTableHeader;
|
import javax.swing.table.JTableHeader;
|
||||||
import javax.swing.table.TableCellRenderer;
|
import javax.swing.table.TableCellRenderer;
|
||||||
@@ -114,6 +114,11 @@ public class FlatTableHeaderUI
|
|||||||
public void installUI( JComponent c ) {
|
public void installUI( JComponent c ) {
|
||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
|
// replace cell renderer pane
|
||||||
|
header.remove( rendererPane );
|
||||||
|
rendererPane = new FlatTableHeaderCellRendererPane();
|
||||||
|
header.add( rendererPane );
|
||||||
|
|
||||||
installStyle();
|
installStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,16 +270,8 @@ public class FlatTableHeaderUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// temporary use own default renderer
|
|
||||||
FlatTableCellHeaderRenderer tempRenderer = new FlatTableCellHeaderRenderer( header.getDefaultRenderer() );
|
|
||||||
header.setDefaultRenderer( tempRenderer );
|
|
||||||
|
|
||||||
// paint header
|
// paint header
|
||||||
super.paint( g, c );
|
super.paint( g, c );
|
||||||
|
|
||||||
// restore default renderer
|
|
||||||
tempRenderer.reset();
|
|
||||||
header.setDefaultRenderer( tempRenderer.delegate );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSystemDefaultRenderer( Object headerRenderer ) {
|
private boolean isSystemDefaultRenderer( Object headerRenderer ) {
|
||||||
@@ -332,119 +329,129 @@ public class FlatTableHeaderUI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class FlatTableCellHeaderRenderer ----------------------------------
|
//---- class FlatTableHeaderCellRendererPane ------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A delegating header renderer that is only used to paint hover and pressed
|
* Cell renderer pane that is used to paint hover and pressed background/foreground
|
||||||
* background/foreground and to paint sort arrows at top, bottom or left position.
|
* and to paint sort arrows at top, bottom or left position.
|
||||||
*/
|
*/
|
||||||
private class FlatTableCellHeaderRenderer
|
private class FlatTableHeaderCellRendererPane
|
||||||
implements TableCellRenderer, Border, UIResource
|
extends CellRendererPane
|
||||||
{
|
{
|
||||||
private final TableCellRenderer delegate;
|
private final Icon ascendingSortIcon;
|
||||||
|
private final Icon descendingSortIcon;
|
||||||
|
|
||||||
private JLabel l;
|
public FlatTableHeaderCellRendererPane() {
|
||||||
private Color oldBackground;
|
ascendingSortIcon = UIManager.getIcon( "Table.ascendingSortIcon" );
|
||||||
private Color oldForeground;
|
descendingSortIcon = UIManager.getIcon( "Table.descendingSortIcon" );
|
||||||
private Boolean oldOpaque;
|
|
||||||
private int oldHorizontalTextPosition = -1;
|
|
||||||
private Border origBorder;
|
|
||||||
private Icon sortIcon;
|
|
||||||
|
|
||||||
FlatTableCellHeaderRenderer( TableCellRenderer delegate ) {
|
|
||||||
this.delegate = delegate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected,
|
public void paintComponent( Graphics g, Component c, Container p, int x, int y, int w, int h, boolean shouldValidate ) {
|
||||||
boolean hasFocus, int row, int column )
|
if( !(c instanceof JLabel) ) {
|
||||||
{
|
super.paintComponent( g, c, p, x, y, w, h, shouldValidate );
|
||||||
Component c = delegate.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column );
|
return;
|
||||||
if( !(c instanceof JLabel) )
|
}
|
||||||
return c;
|
|
||||||
|
|
||||||
l = (JLabel) c;
|
JLabel l = (JLabel) c;
|
||||||
|
Color oldBackground = null;
|
||||||
|
Color oldForeground = null;
|
||||||
|
Boolean oldOpaque = null;
|
||||||
|
Icon oldIcon = null;
|
||||||
|
int oldHorizontalTextPosition = -1;
|
||||||
|
|
||||||
// hover and pressed background/foreground
|
// hover and pressed background/foreground
|
||||||
TableColumn draggedColumn = header.getDraggedColumn();
|
TableColumn draggedColumn = header.getDraggedColumn();
|
||||||
Color background = null;
|
Color background = null;
|
||||||
Color foreground = null;
|
Color foreground = null;
|
||||||
if( draggedColumn != null && header.getTable().convertColumnIndexToView( draggedColumn.getModelIndex() ) == column ) {
|
if( draggedColumn != null &&
|
||||||
|
header.getTable().convertColumnIndexToView( draggedColumn.getModelIndex() )
|
||||||
|
== getColumn( x - header.getDraggedDistance(), w ) )
|
||||||
|
{
|
||||||
background = pressedBackground;
|
background = pressedBackground;
|
||||||
foreground = pressedForeground;
|
foreground = pressedForeground;
|
||||||
} else if( getRolloverColumn() == column ) {
|
} else if( getRolloverColumn() >= 0 && getRolloverColumn() == getColumn( x, w ) ) {
|
||||||
background = hoverBackground;
|
background = hoverBackground;
|
||||||
foreground = hoverForeground;
|
foreground = hoverForeground;
|
||||||
}
|
}
|
||||||
if( background != null ) {
|
if( background != null ) {
|
||||||
if( oldBackground == null )
|
|
||||||
oldBackground = l.getBackground();
|
oldBackground = l.getBackground();
|
||||||
if( oldOpaque == null )
|
|
||||||
oldOpaque = l.isOpaque();
|
oldOpaque = l.isOpaque();
|
||||||
l.setBackground( FlatUIUtils.deriveColor( background, header.getBackground() ) );
|
l.setBackground( FlatUIUtils.deriveColor( background, header.getBackground() ) );
|
||||||
l.setOpaque( true );
|
l.setOpaque( true );
|
||||||
}
|
}
|
||||||
if( foreground != null ) {
|
if( foreground != null ) {
|
||||||
if( oldForeground == null )
|
|
||||||
oldForeground = l.getForeground();
|
oldForeground = l.getForeground();
|
||||||
l.setForeground( FlatUIUtils.deriveColor( foreground, header.getForeground() ) );
|
l.setForeground( FlatUIUtils.deriveColor( foreground, header.getForeground() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort icon
|
// sort icon position
|
||||||
|
Icon icon = l.getIcon();
|
||||||
|
boolean isSortIcon = (icon != null && (icon == ascendingSortIcon || icon == descendingSortIcon));
|
||||||
|
if( isSortIcon ) {
|
||||||
if( sortIconPosition == SwingConstants.LEFT ) {
|
if( sortIconPosition == SwingConstants.LEFT ) {
|
||||||
// left
|
// left
|
||||||
if( oldHorizontalTextPosition < 0 )
|
|
||||||
oldHorizontalTextPosition = l.getHorizontalTextPosition();
|
oldHorizontalTextPosition = l.getHorizontalTextPosition();
|
||||||
l.setHorizontalTextPosition( SwingConstants.RIGHT );
|
l.setHorizontalTextPosition( SwingConstants.RIGHT );
|
||||||
} else if( sortIconPosition == SwingConstants.TOP || sortIconPosition == SwingConstants.BOTTOM ) {
|
} else if( sortIconPosition == SwingConstants.TOP || sortIconPosition == SwingConstants.BOTTOM ) {
|
||||||
// top or bottom
|
// top or bottom
|
||||||
sortIcon = l.getIcon();
|
oldIcon = icon;
|
||||||
origBorder = l.getBorder();
|
|
||||||
l.setIcon( null );
|
l.setIcon( null );
|
||||||
l.setBorder( this );
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return l;
|
// paint renderer component
|
||||||
|
super.paintComponent( g, c, p, x, y, w, h, shouldValidate );
|
||||||
|
|
||||||
|
// paint top or bottom sort icon
|
||||||
|
if( isSortIcon && (sortIconPosition == SwingConstants.TOP || sortIconPosition == SwingConstants.BOTTOM) ) {
|
||||||
|
int xi = x + ((w - icon.getIconWidth()) / 2);
|
||||||
|
int yi = (sortIconPosition == SwingConstants.TOP)
|
||||||
|
? y + UIScale.scale( 1 )
|
||||||
|
: y + height - icon.getIconHeight()
|
||||||
|
- 1 // for gap
|
||||||
|
- (int) (1 * UIScale.getUserScaleFactor()); // for bottom border
|
||||||
|
icon.paintIcon( c, g, xi, yi );
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
// restore modified renderer component properties
|
||||||
if( l == null )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if( oldBackground != null )
|
if( oldBackground != null )
|
||||||
l.setBackground( oldBackground );
|
l.setBackground( oldBackground );
|
||||||
if( oldForeground != null )
|
if( oldForeground != null )
|
||||||
l.setForeground( oldForeground );
|
l.setForeground( oldForeground );
|
||||||
if( oldOpaque != null )
|
if( oldOpaque != null )
|
||||||
l.setOpaque( oldOpaque );
|
l.setOpaque( oldOpaque );
|
||||||
|
if( oldIcon != null )
|
||||||
|
l.setIcon( oldIcon );
|
||||||
if( oldHorizontalTextPosition >= 0 )
|
if( oldHorizontalTextPosition >= 0 )
|
||||||
l.setHorizontalTextPosition( oldHorizontalTextPosition );
|
l.setHorizontalTextPosition( oldHorizontalTextPosition );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
* Get column index for given coordinates.
|
||||||
if( origBorder != null )
|
*/
|
||||||
origBorder.paintBorder( c, g, x, y, width, height );
|
private int getColumn( int x, int width ) {
|
||||||
|
TableColumnModel columnModel = header.getColumnModel();
|
||||||
|
int columnCount = columnModel.getColumnCount();
|
||||||
|
boolean ltr = header.getComponentOrientation().isLeftToRight();
|
||||||
|
int cx = ltr ? 0 : getWidthInRightToLef();
|
||||||
|
|
||||||
if( sortIcon != null ) {
|
for( int i = 0; i < columnCount; i++ ) {
|
||||||
int xi = x + ((width - sortIcon.getIconWidth()) / 2);
|
int cw = columnModel.getColumn( i ).getWidth();
|
||||||
int yi = (sortIconPosition == SwingConstants.TOP)
|
if( x == cx - (ltr ? 0 : cw) && width == cw )
|
||||||
? y + UIScale.scale( 1 )
|
return i;
|
||||||
: y + height - sortIcon.getIconHeight()
|
|
||||||
- 1 // for gap
|
cx += ltr ? cw : -cw;
|
||||||
- (int) (1 * UIScale.getUserScaleFactor()); // for bottom border
|
|
||||||
sortIcon.paintIcon( c, g, xi, yi );
|
|
||||||
}
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// similar to JTableHeader.getWidthInRightToLeft()
|
||||||
public Insets getBorderInsets( Component c ) {
|
private int getWidthInRightToLef() {
|
||||||
return (origBorder != null) ? origBorder.getBorderInsets( c ) : new Insets( 0, 0, 0, 0 );
|
JTable table = header.getTable();
|
||||||
}
|
return (table != null && table.getAutoResizeMode() != JTable.AUTO_RESIZE_OFF)
|
||||||
|
? table.getWidth()
|
||||||
@Override
|
: header.getWidth();
|
||||||
public boolean isBorderOpaque() {
|
|
||||||
return (origBorder != null) ? origBorder.isBorderOpaque() : false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user