From aefed7c481f6dbaae7e772bc60ce387602274c84 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Sat, 28 Nov 2020 22:04:12 +0100 Subject: [PATCH] Table: do not paint last vertical grid line if auto-resize mode is not off (issue #46) --- CHANGELOG.md | 3 + .../com/formdev/flatlaf/ui/FlatTableUI.java | 58 +++++++++++++++++++ .../flatlaf/testing/FlatComponents2Test.java | 51 +++++++++++++--- .../flatlaf/testing/FlatComponents2Test.jfd | 45 ++++++++++---- 4 files changed, 139 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5830963a..e5970c8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ FlatLaf Change Log and "One Dark" themes. - TabbedPane: Support hiding tab area if it contains only one tab. (set client property `JTabbedPane.hideTabAreaWithOneTab` to `true`) +- Table: Do not paint last vertical grid line if auto-resize mode is not off. + (issue #46) + #### Fixed bugs 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 51aff9ca..ed67da8a 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 @@ -19,15 +19,19 @@ package com.formdev.flatlaf.ui; import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; +import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import javax.swing.JCheckBox; import javax.swing.JComponent; +import javax.swing.JTable; import javax.swing.LookAndFeel; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicTableUI; import javax.swing.table.TableCellRenderer; +import com.formdev.flatlaf.util.Graphics2DProxy; import com.formdev.flatlaf.util.UIScale; /** @@ -203,4 +207,58 @@ public class FlatTableUI table.setSelectionForeground( selectionInactiveForeground ); } } + + @Override + public void paint( Graphics g, JComponent c ) { + if( table.getShowVerticalLines() ) { + // 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 + + boolean hideLastVerticalLine = + table.getAutoResizeMode() != JTable.AUTO_RESIZE_OFF || + !table.getComponentOrientation().isLeftToRight(); + int tableWidth = table.getWidth(); + + // Java 8 uses drawLine() to paint grid lines + // Java 9+ uses fillRect() to paint grid lines + g = new Graphics2DProxy( (Graphics2D) g ) { + @Override + public void drawLine( int x1, int y1, int x2, int y2 ) { + // do not paint last vertical line + if( hideLastVerticalLine && + x1 == x2 && y1 == 0 && x1 == tableWidth - 1 && + wasInvokedFromPaintGrid() ) + return; + + super.drawLine( x1, y1, x2, y2 ); + } + + @Override + public void fillRect( int x, int y, int width, int height ) { + // do not paint last vertical line + if( hideLastVerticalLine && + width == 1 && y == 0 && x == tableWidth - 1 && + wasInvokedFromPaintGrid() ) + return; + + super.fillRect( x, y, width, height ); + } + + private boolean wasInvokedFromPaintGrid() { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + for( int i = 0; i < 10 || i < stackTrace.length; i++ ) { + if( "javax.swing.plaf.basic.BasicTableUI".equals( stackTrace[i].getClassName() ) && + "paintGrid".equals( stackTrace[i].getMethodName() ) ) + return true; + } + return false; + } + }; + } + + super.paint( g, c ); + } } 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 dc01cd27..370ad4bc 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 @@ -121,6 +121,21 @@ public class FlatComponents2Test tableModel.setRowCount( (Integer) tableRowCountSpinner.getValue() ); } + private void autoResizeModeChanged() { + int autoResizeMode = JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS; + Object sel = autoResizeModeField.getSelectedItem(); + if( sel instanceof String ) { + switch( (String) sel ) { + case "off": autoResizeMode = JTable.AUTO_RESIZE_OFF; break; + case "nextColumn": autoResizeMode = JTable.AUTO_RESIZE_NEXT_COLUMN; break; + case "subsequentColumns": autoResizeMode = JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS; break; + case "lastColumn": autoResizeMode = JTable.AUTO_RESIZE_LAST_COLUMN; break; + case "allColumns": autoResizeMode = JTable.AUTO_RESIZE_ALL_COLUMNS; break; + } + } + table1.setAutoResizeMode( autoResizeMode ); + } + private void dndChanged() { boolean dnd = dndCheckBox.isSelected(); list1.setDragEnabled( dnd ); @@ -225,6 +240,8 @@ public class FlatComponents2Test JPanel tableOptionsPanel = new JPanel(); JLabel tableRowCountLabel = new JLabel(); tableRowCountSpinner = new JSpinner(); + JLabel autoResizeModeLabel = new JLabel(); + autoResizeModeField = new JComboBox<>(); showHorizontalLinesCheckBox = new JCheckBox(); showVerticalLinesCheckBox = new JCheckBox(); intercellSpacingCheckBox = new JCheckBox(); @@ -367,6 +384,7 @@ public class FlatComponents2Test "[90,fill]", // rows "[]" + + "[]" + "[]0" + "[]0" + "[]0" + @@ -385,47 +403,63 @@ public class FlatComponents2Test tableRowCountSpinner.addChangeListener(e -> tableRowCountChanged()); tableOptionsPanel.add(tableRowCountSpinner, "cell 1 0"); + //---- autoResizeModeLabel ---- + autoResizeModeLabel.setText("Auto resize mode:"); + tableOptionsPanel.add(autoResizeModeLabel, "cell 0 1"); + + //---- autoResizeModeField ---- + autoResizeModeField.setModel(new DefaultComboBoxModel<>(new String[] { + "off", + "nextColumn", + "subsequentColumns", + "lastColumn", + "allColumns" + })); + autoResizeModeField.setSelectedIndex(2); + autoResizeModeField.addActionListener(e -> autoResizeModeChanged()); + tableOptionsPanel.add(autoResizeModeField, "cell 1 1"); + //---- showHorizontalLinesCheckBox ---- showHorizontalLinesCheckBox.setText("show horizontal lines"); showHorizontalLinesCheckBox.addActionListener(e -> showHorizontalLinesChanged()); - tableOptionsPanel.add(showHorizontalLinesCheckBox, "cell 0 1 2 1"); + tableOptionsPanel.add(showHorizontalLinesCheckBox, "cell 0 2 2 1"); //---- showVerticalLinesCheckBox ---- showVerticalLinesCheckBox.setText("show vertical lines"); showVerticalLinesCheckBox.addActionListener(e -> showVerticalLinesChanged()); - tableOptionsPanel.add(showVerticalLinesCheckBox, "cell 0 2 2 1"); + tableOptionsPanel.add(showVerticalLinesCheckBox, "cell 0 3 2 1"); //---- intercellSpacingCheckBox ---- intercellSpacingCheckBox.setText("intercell spacing"); intercellSpacingCheckBox.addActionListener(e -> intercellSpacingChanged()); - tableOptionsPanel.add(intercellSpacingCheckBox, "cell 0 3 2 1"); + tableOptionsPanel.add(intercellSpacingCheckBox, "cell 0 4 2 1"); //---- redGridColorCheckBox ---- redGridColorCheckBox.setText("red grid color"); redGridColorCheckBox.addActionListener(e -> redGridColorChanged()); - tableOptionsPanel.add(redGridColorCheckBox, "cell 0 4 2 1"); + tableOptionsPanel.add(redGridColorCheckBox, "cell 0 5 2 1"); //---- rowSelectionCheckBox ---- rowSelectionCheckBox.setText("row selection"); rowSelectionCheckBox.setSelected(true); rowSelectionCheckBox.addActionListener(e -> rowSelectionChanged()); - tableOptionsPanel.add(rowSelectionCheckBox, "cell 0 5 2 1"); + tableOptionsPanel.add(rowSelectionCheckBox, "cell 0 6 2 1"); //---- columnSelectionCheckBox ---- columnSelectionCheckBox.setText("column selection"); columnSelectionCheckBox.addActionListener(e -> columnSelectionChanged()); - tableOptionsPanel.add(columnSelectionCheckBox, "cell 0 6 2 1"); + tableOptionsPanel.add(columnSelectionCheckBox, "cell 0 7 2 1"); //---- dndCheckBox ---- dndCheckBox.setText("enable drag and drop"); dndCheckBox.setMnemonic('D'); dndCheckBox.addActionListener(e -> dndChanged()); - tableOptionsPanel.add(dndCheckBox, "cell 0 7 2 1"); + tableOptionsPanel.add(dndCheckBox, "cell 0 8 2 1"); //---- tableHeaderButtonCheckBox ---- tableHeaderButtonCheckBox.setText("show button in table header"); tableHeaderButtonCheckBox.addActionListener(e -> tableHeaderButtonChanged()); - tableOptionsPanel.add(tableHeaderButtonCheckBox, "cell 0 8 2 1"); + tableOptionsPanel.add(tableHeaderButtonCheckBox, "cell 0 9 2 1"); } add(tableOptionsPanel, "cell 3 3"); // JFormDesigner - End of component initialization //GEN-END:initComponents @@ -441,6 +475,7 @@ public class FlatComponents2Test private JScrollPane scrollPane5; private JTable table1; private JSpinner tableRowCountSpinner; + private JComboBox autoResizeModeField; private JCheckBox showHorizontalLinesCheckBox; private JCheckBox showVerticalLinesCheckBox; private JCheckBox intercellSpacingCheckBox; 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 96e3a3ac..3fa4d2c0 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.2.0.298" Java: "14" encoding: "UTF-8" +JFDML JFormDesigner: "7.0.2.0.298" Java: "15" encoding: "UTF-8" new FormModel { contentType: "form/swing" @@ -175,7 +175,7 @@ new FormModel { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { "$layoutConstraints": "hidemode 3" "$columnConstraints": "[][90,fill]" - "$rowConstraints": "[][]0[]0[]0[]0[]0[]0[]0[]" + "$rowConstraints": "[][][]0[]0[]0[]0[]0[]0[]0[]" } ) { name: "tableOptionsPanel" add( new FormComponent( "javax.swing.JLabel" ) { @@ -194,6 +194,31 @@ new FormModel { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 1 0" } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "autoResizeModeLabel" + "text": "Auto resize mode:" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 1" + } ) + add( new FormComponent( "javax.swing.JComboBox" ) { + name: "autoResizeModeField" + "model": new javax.swing.DefaultComboBoxModel { + selectedItem: "off" + addElement( "off" ) + addElement( "nextColumn" ) + addElement( "subsequentColumns" ) + addElement( "lastColumn" ) + addElement( "allColumns" ) + } + "selectedIndex": 2 + auxiliary() { + "JavaCodeGenerator.variableLocal": false + "JavaCodeGenerator.typeParameters": "String" + } + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "autoResizeModeChanged", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 1" + } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "showHorizontalLinesCheckBox" "text": "show horizontal lines" @@ -202,7 +227,7 @@ new FormModel { } addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showHorizontalLinesChanged", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 1 2 1" + "value": "cell 0 2 2 1" } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "showVerticalLinesCheckBox" @@ -212,7 +237,7 @@ new FormModel { } addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showVerticalLinesChanged", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 2 2 1" + "value": "cell 0 3 2 1" } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "intercellSpacingCheckBox" @@ -222,7 +247,7 @@ new FormModel { } addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "intercellSpacingChanged", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 3 2 1" + "value": "cell 0 4 2 1" } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "redGridColorCheckBox" @@ -232,7 +257,7 @@ new FormModel { } addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "redGridColorChanged", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 4 2 1" + "value": "cell 0 5 2 1" } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "rowSelectionCheckBox" @@ -243,7 +268,7 @@ new FormModel { } addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "rowSelectionChanged", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 5 2 1" + "value": "cell 0 6 2 1" } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "columnSelectionCheckBox" @@ -253,7 +278,7 @@ new FormModel { } addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "columnSelectionChanged", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 6 2 1" + "value": "cell 0 7 2 1" } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "dndCheckBox" @@ -264,7 +289,7 @@ new FormModel { } addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "dndChanged", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 7 2 1" + "value": "cell 0 8 2 1" } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "tableHeaderButtonCheckBox" @@ -274,7 +299,7 @@ new FormModel { } addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tableHeaderButtonChanged", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 8 2 1" + "value": "cell 0 9 2 1" } ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 3 3"