mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-07 14:30:56 +03:00
Table and TableHeader: fixed missing right vertical grid line if using table as row header in scroll pane (issues #152 and #46)
This commit is contained in:
@@ -5,6 +5,8 @@ FlatLaf Change Log
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- Table and TableHeader: Fixed missing right vertical grid line if using table
|
||||
as row header in scroll pane. (issues #152 and #46)
|
||||
- TableHeader: Fixed position of column separators in right-to-left component
|
||||
orientation.
|
||||
- SwingX: Fixed striping background highlighting color (e.g. alternating table
|
||||
|
||||
@@ -31,6 +31,7 @@ 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;
|
||||
@@ -148,6 +149,9 @@ public class FlatTableHeaderUI
|
||||
float bottomLineIndent = lineWidth * 3;
|
||||
TableColumnModel columnModel = header.getColumnModel();
|
||||
int columnCount = columnModel.getColumnCount();
|
||||
int sepCount = columnCount;
|
||||
if( hideLastVerticalLine() )
|
||||
sepCount--;
|
||||
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
try {
|
||||
@@ -160,24 +164,30 @@ public class FlatTableHeaderUI
|
||||
// paint column separator lines
|
||||
g2.setColor( separatorColor );
|
||||
|
||||
int sepCount = columnCount;
|
||||
if( header.getTable() != null && header.getTable().getAutoResizeMode() != JTable.AUTO_RESIZE_OFF && !isVerticalScrollBarVisible() )
|
||||
sepCount--;
|
||||
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, topLineIndent, lineWidth, height - bottomLineIndent ) );
|
||||
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),
|
||||
topLineIndent, lineWidth, height - bottomLineIndent ) );
|
||||
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();
|
||||
@@ -234,20 +244,30 @@ public class FlatTableHeaderUI
|
||||
return size;
|
||||
}
|
||||
|
||||
private boolean isVerticalScrollBarVisible() {
|
||||
JScrollPane scrollPane = getScrollPane();
|
||||
return (scrollPane != null && scrollPane.getVerticalScrollBar() != null)
|
||||
? scrollPane.getVerticalScrollBar().isVisible()
|
||||
: false;
|
||||
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;
|
||||
}
|
||||
|
||||
private JScrollPane getScrollPane() {
|
||||
Container parent = header.getParent();
|
||||
if( parent == null )
|
||||
return null;
|
||||
protected boolean hideTrailingVerticalLine() {
|
||||
Container viewport = header.getParent();
|
||||
Container viewportParent = (viewport != null) ? viewport.getParent() : null;
|
||||
if( !(viewportParent instanceof JScrollPane) )
|
||||
return false;
|
||||
|
||||
parent = parent.getParent();
|
||||
return (parent instanceof JScrollPane) ? (JScrollPane) parent : null;
|
||||
JScrollPane scrollPane = (JScrollPane) viewportParent;
|
||||
return viewport == scrollPane.getColumnHeader() &&
|
||||
scrollPane.getCorner( ScrollPaneConstants.UPPER_TRAILING_CORNER ) == null;
|
||||
}
|
||||
|
||||
//---- class FlatTableCellHeaderRenderer ----------------------------------
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.Graphics;
|
||||
@@ -26,8 +27,10 @@ import java.awt.event.FocusListener;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JViewport;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicTableUI;
|
||||
@@ -215,16 +218,11 @@ public class FlatTableUI
|
||||
boolean verticalLines = table.getShowVerticalLines();
|
||||
if( horizontalLines || verticalLines ) {
|
||||
// fix grid painting issues in BasicTableUI
|
||||
// - do not paint last vertical grid line if auto-resize mode is not off
|
||||
// - in right-to-left component orientation, do not paint last vertical grid line
|
||||
// in any auto-resize mode; can not paint on left side of table because
|
||||
// cells are painted over left line
|
||||
// - do not paint last vertical grid line if line is on right edge of scroll pane
|
||||
// - fix unstable grid line thickness when scaled at 125%, 150%, 175%, 225%, ...
|
||||
// which paints either 1px or 2px lines depending on location
|
||||
|
||||
boolean hideLastVerticalLine =
|
||||
table.getAutoResizeMode() != JTable.AUTO_RESIZE_OFF ||
|
||||
!table.getComponentOrientation().isLeftToRight();
|
||||
boolean hideLastVerticalLine = hideLastVerticalLine();
|
||||
int tableWidth = table.getWidth();
|
||||
|
||||
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
|
||||
@@ -281,4 +279,26 @@ public class FlatTableUI
|
||||
|
||||
super.paint( g, c );
|
||||
}
|
||||
|
||||
protected boolean hideLastVerticalLine() {
|
||||
Container viewport = SwingUtilities.getUnwrappedParent( table );
|
||||
Container viewportParent = (viewport != null) ? viewport.getParent() : null;
|
||||
if( !(viewportParent instanceof JScrollPane) )
|
||||
return false;
|
||||
|
||||
// do not hide last vertical line if table is smaller than viewport
|
||||
if( table.getX() + table.getWidth() < viewport.getWidth() )
|
||||
return false;
|
||||
|
||||
// in left-to-right:
|
||||
// - do not hide last vertical line if table used as row header in scroll pane
|
||||
// in right-to-left:
|
||||
// - hide last vertical line if table used as row header in scroll pane
|
||||
// - do not hide last vertical line if table is in center and scroll pane has row header
|
||||
JScrollPane scrollPane = (JScrollPane) viewportParent;
|
||||
JViewport rowHeader = scrollPane.getRowHeader();
|
||||
return scrollPane.getComponentOrientation().isLeftToRight()
|
||||
? (viewport != rowHeader)
|
||||
: (viewport == rowHeader || rowHeader == null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
package com.formdev.flatlaf.testing;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.ComponentOrientation;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
@@ -29,10 +31,13 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.TableModelEvent;
|
||||
import javax.swing.event.TableModelListener;
|
||||
import javax.swing.table.*;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
import com.formdev.flatlaf.icons.FlatMenuArrowIcon;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
import net.miginfocom.swing.*;
|
||||
import org.jdesktop.swingx.JXTable;
|
||||
import org.jdesktop.swingx.JXTreeTable;
|
||||
@@ -53,6 +58,7 @@ public class FlatComponents2Test
|
||||
public static void main( String[] args ) {
|
||||
SwingUtilities.invokeLater( () -> {
|
||||
FlatTestFrame frame = FlatTestFrame.create( args, "FlatComponents2Test" );
|
||||
frame.useApplyComponentOrientation = true;
|
||||
frame.showFrame( FlatComponents2Test::new );
|
||||
} );
|
||||
}
|
||||
@@ -60,7 +66,9 @@ public class FlatComponents2Test
|
||||
private final TestListModel listModel;
|
||||
private final TestTreeModel treeModel;
|
||||
private final TestTableModel tableModel;
|
||||
private final JTable[] allTables;
|
||||
private final List<JTable> allTables = new ArrayList<>();
|
||||
private final List<JTable> allTablesInclRowHeader = new ArrayList<>();
|
||||
private JTable rowHeaderTable1;
|
||||
|
||||
FlatComponents2Test() {
|
||||
initComponents();
|
||||
@@ -96,7 +104,10 @@ public class FlatComponents2Test
|
||||
xTreeTable1.setTreeTableModel( new FileSystemModel( new File( "." ) ) );
|
||||
xTreeTable1.setHighlighters( simpleStriping, magenta, rollover, shading );
|
||||
|
||||
allTables = new JTable[] { table1, xTable1, xTreeTable1 };
|
||||
allTables.add( table1 );
|
||||
allTables.add( xTable1 );
|
||||
allTables.add( xTreeTable1 );
|
||||
allTablesInclRowHeader.addAll( allTables );
|
||||
|
||||
expandTree( tree1 );
|
||||
expandTree( tree2 );
|
||||
@@ -219,6 +230,7 @@ public class FlatComponents2Test
|
||||
JButton button = null;
|
||||
if( show ) {
|
||||
button = new JButton( new FlatMenuArrowIcon() );
|
||||
button.applyComponentOrientation( getComponentOrientation() );
|
||||
button.addActionListener( e -> {
|
||||
JOptionPane.showMessageDialog( this, "hello" );
|
||||
} );
|
||||
@@ -237,25 +249,72 @@ public class FlatComponents2Test
|
||||
}
|
||||
|
||||
private void showHorizontalLinesChanged() {
|
||||
for( JTable table : allTables )
|
||||
for( JTable table : allTablesInclRowHeader )
|
||||
table.setShowHorizontalLines( showHorizontalLinesCheckBox.isSelected() );
|
||||
}
|
||||
|
||||
private void showVerticalLinesChanged() {
|
||||
for( JTable table : allTables )
|
||||
for( JTable table : allTablesInclRowHeader )
|
||||
table.setShowVerticalLines( showVerticalLinesCheckBox.isSelected() );
|
||||
}
|
||||
|
||||
private void intercellSpacingChanged() {
|
||||
for( JTable table : allTables )
|
||||
for( JTable table : allTablesInclRowHeader )
|
||||
table.setIntercellSpacing( intercellSpacingCheckBox.isSelected() ? new Dimension( 1, 1 ) : new Dimension() );
|
||||
}
|
||||
|
||||
private void redGridColorChanged() {
|
||||
for( JTable table : allTables )
|
||||
for( JTable table : allTablesInclRowHeader )
|
||||
table.setGridColor( redGridColorCheckBox.isSelected() ? Color.red : UIManager.getColor( "Table.gridColor" ) );
|
||||
}
|
||||
|
||||
private void rowHeaderChanged() {
|
||||
if( rowHeaderCheckBox.isSelected() ) {
|
||||
TestTableRowHeaderModel rowHeaderModel = new TestTableRowHeaderModel( tableModel );
|
||||
rowHeaderTable1 = new JTable( rowHeaderModel );
|
||||
rowHeaderTable1.setPreferredScrollableViewportSize( UIScale.scale( new Dimension( 50, 50 ) ) );
|
||||
rowHeaderTable1.setSelectionModel( table1.getSelectionModel() );
|
||||
|
||||
DefaultTableCellRenderer rowHeaderRenderer = new DefaultTableCellRenderer();
|
||||
rowHeaderRenderer.setHorizontalAlignment( JLabel.CENTER );
|
||||
rowHeaderTable1.setDefaultRenderer( Object.class, rowHeaderRenderer );
|
||||
table1ScrollPane.setRowHeaderView( rowHeaderTable1 );
|
||||
|
||||
JViewport headerViewport = new JViewport();
|
||||
headerViewport.setView( rowHeaderTable1.getTableHeader() );
|
||||
table1ScrollPane.setCorner( ScrollPaneConstants.UPPER_LEADING_CORNER, headerViewport );
|
||||
|
||||
table1ScrollPane.applyComponentOrientation( getComponentOrientation() );
|
||||
|
||||
allTablesInclRowHeader.add( rowHeaderTable1 );
|
||||
|
||||
showHorizontalLinesChanged();
|
||||
showVerticalLinesChanged();
|
||||
intercellSpacingChanged();
|
||||
redGridColorChanged();
|
||||
} else {
|
||||
table1ScrollPane.setRowHeader( null );
|
||||
table1ScrollPane.setCorner( ScrollPaneConstants.UPPER_LEADING_CORNER, null );
|
||||
allTablesInclRowHeader.remove( rowHeaderTable1 );
|
||||
|
||||
((TestTableRowHeaderModel)rowHeaderTable1.getModel()).dispose();
|
||||
rowHeaderTable1 = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyComponentOrientation( ComponentOrientation o ) {
|
||||
super.applyComponentOrientation( o );
|
||||
|
||||
// swap upper right and left corners (other corners are not used in this app)
|
||||
Component leftCorner = table1ScrollPane.getCorner( ScrollPaneConstants.UPPER_LEFT_CORNER );
|
||||
Component rightCorner = table1ScrollPane.getCorner( ScrollPaneConstants.UPPER_RIGHT_CORNER );
|
||||
table1ScrollPane.setCorner( ScrollPaneConstants.UPPER_LEFT_CORNER, null );
|
||||
table1ScrollPane.setCorner( ScrollPaneConstants.UPPER_RIGHT_CORNER, null );
|
||||
table1ScrollPane.setCorner( ScrollPaneConstants.UPPER_LEFT_CORNER, rightCorner );
|
||||
table1ScrollPane.setCorner( ScrollPaneConstants.UPPER_RIGHT_CORNER, leftCorner );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUI() {
|
||||
super.updateUI();
|
||||
@@ -300,6 +359,7 @@ public class FlatComponents2Test
|
||||
JLabel autoResizeModeLabel = new JLabel();
|
||||
autoResizeModeField = new JComboBox<>();
|
||||
showHorizontalLinesCheckBox = new JCheckBox();
|
||||
rowHeaderCheckBox = new JCheckBox();
|
||||
showVerticalLinesCheckBox = new JCheckBox();
|
||||
intercellSpacingCheckBox = new JCheckBox();
|
||||
redGridColorCheckBox = new JCheckBox();
|
||||
@@ -461,7 +521,7 @@ public class FlatComponents2Test
|
||||
panel3.add(tableRowCountLabel, "cell 0 2");
|
||||
|
||||
//---- tableRowCountSpinner ----
|
||||
tableRowCountSpinner.setModel(new SpinnerNumberModel(20, 0, null, 10));
|
||||
tableRowCountSpinner.setModel(new SpinnerNumberModel(20, 0, null, 5));
|
||||
tableRowCountSpinner.addChangeListener(e -> tableRowCountChanged());
|
||||
panel3.add(tableRowCountSpinner, "cell 0 3");
|
||||
}
|
||||
@@ -515,6 +575,11 @@ public class FlatComponents2Test
|
||||
showHorizontalLinesCheckBox.addActionListener(e -> showHorizontalLinesChanged());
|
||||
tableOptionsPanel.add(showHorizontalLinesCheckBox, "cell 0 1");
|
||||
|
||||
//---- rowHeaderCheckBox ----
|
||||
rowHeaderCheckBox.setText("row header");
|
||||
rowHeaderCheckBox.addActionListener(e -> rowHeaderChanged());
|
||||
tableOptionsPanel.add(rowHeaderCheckBox, "cell 1 1");
|
||||
|
||||
//---- showVerticalLinesCheckBox ----
|
||||
showVerticalLinesCheckBox.setText("show vertical lines");
|
||||
showVerticalLinesCheckBox.addActionListener(e -> showVerticalLinesChanged());
|
||||
@@ -588,6 +653,7 @@ public class FlatComponents2Test
|
||||
private JTable table1;
|
||||
private JComboBox<String> autoResizeModeField;
|
||||
private JCheckBox showHorizontalLinesCheckBox;
|
||||
private JCheckBox rowHeaderCheckBox;
|
||||
private JCheckBox showVerticalLinesCheckBox;
|
||||
private JCheckBox intercellSpacingCheckBox;
|
||||
private JCheckBox redGridColorCheckBox;
|
||||
@@ -885,4 +951,56 @@ public class FlatComponents2Test
|
||||
fireTableCellUpdated( rowIndex, columnIndex );
|
||||
}
|
||||
}
|
||||
|
||||
//---- TestTableRowHeaderModel --------------------------------------------
|
||||
|
||||
private class TestTableRowHeaderModel
|
||||
extends AbstractTableModel
|
||||
implements TableModelListener
|
||||
{
|
||||
private final TableModel model;
|
||||
|
||||
TestTableRowHeaderModel( TableModel model ) {
|
||||
this.model = model;
|
||||
|
||||
model.addTableModelListener( this );
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
model.removeTableModelListener( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return model.getRowCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName( int columnIndex ) {
|
||||
return "Row #";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt( int rowIndex, int columnIndex ) {
|
||||
return rowIndex + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tableChanged( TableModelEvent e ) {
|
||||
switch( e.getType() ) {
|
||||
case TableModelEvent.INSERT:
|
||||
fireTableRowsInserted( e.getFirstRow(), e.getLastRow() );
|
||||
break;
|
||||
|
||||
case TableModelEvent.DELETE:
|
||||
fireTableRowsDeleted( e.getFirstRow(), e.getLastRow() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ new FormModel {
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JSpinner" ) {
|
||||
name: "listRowCountSpinner"
|
||||
"model": &SpinnerNumberModel0 new javax.swing.SpinnerNumberModel {
|
||||
"model": new javax.swing.SpinnerNumberModel {
|
||||
minimum: 0
|
||||
stepSize: 10
|
||||
value: 20
|
||||
@@ -184,7 +184,11 @@ new FormModel {
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JSpinner" ) {
|
||||
name: "tableRowCountSpinner"
|
||||
"model": #SpinnerNumberModel0
|
||||
"model": new javax.swing.SpinnerNumberModel {
|
||||
minimum: 0
|
||||
stepSize: 5
|
||||
value: 20
|
||||
}
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": false
|
||||
}
|
||||
@@ -251,6 +255,16 @@ new FormModel {
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 1"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||
name: "rowHeaderCheckBox"
|
||||
"text": "row header"
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": false
|
||||
}
|
||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "rowHeaderChanged", false ) )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 1"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||
name: "showVerticalLinesCheckBox"
|
||||
"text": "show vertical lines"
|
||||
|
||||
Reference in New Issue
Block a user