Table: support rounded selection (issue #844)

This commit is contained in:
Karl Tauber
2024-06-24 18:36:44 +02:00
parent 72a4c00e72
commit 9ffda72ae3
11 changed files with 644 additions and 55 deletions

View File

@@ -47,6 +47,7 @@ import com.formdev.flatlaf.FlatLaf;
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;
@@ -164,6 +165,8 @@ public class FlatComponents2Test
JComboBox<String> editableComboBox = new JComboBox<>( months );
editableComboBox.setEditable( true );
cm.getColumn(3).setCellEditor( new DefaultCellEditor( editableComboBox ) );
// table.setDefaultRenderer( Object.class, new TestLabelRoundedTableCellRenderer() );
}
private void expandTree( JTree tree ) {
@@ -229,6 +232,65 @@ public class FlatComponents2Test
FlatLaf.updateUILater();
}
private void roundedSelectionChanged() {
String style = roundedSelectionCheckBox.isSelected() ? "selectionArc: 12; " : "";
int left = leftSelectionInsetsCheckBox.isSelected() ? 2 : 0;
int right = rightSelectionInsetsCheckBox.isSelected() ? 2 : 0;
int top = topSelectionInsetsCheckBox.isSelected() ? 2 : 0;
int bottom = bottomSelectionInsetsCheckBox.isSelected() ? 2 : 0;
if( left > 0 || right > 0 || top > 0 || bottom > 0 )
style += "selectionInsets: " + top + ',' + left + ',' + bottom + ',' + right;
if( style.isEmpty() )
style = null;
list1.putClientProperty( FlatClientProperties.STYLE, style );
list2.putClientProperty( FlatClientProperties.STYLE, style );
tree1.putClientProperty( FlatClientProperties.STYLE, style );
tree2.putClientProperty( FlatClientProperties.STYLE, style );
xTree1.putClientProperty( FlatClientProperties.STYLE, style );
checkBoxTree1.putClientProperty( FlatClientProperties.STYLE, style );
table1.putClientProperty( FlatClientProperties.STYLE, style );
xTable1.putClientProperty( FlatClientProperties.STYLE, style );
xTreeTable1.putClientProperty( FlatClientProperties.STYLE, style );
// initial selection
if( style != null ) {
initSelection( list1 );
initSelection( list2 );
initSelection( tree1 );
initSelection( tree2 );
initSelection( xTree1 );
initSelection( checkBoxTree1 );
initSelection( table1 );
initSelection( xTable1 );
initSelection( xTreeTable1 );
}
if( paintOutsideAlternateRowsCheckBox.isSelected() )
table1ScrollPane.repaint();
}
private static void initSelection( JList<?> list ) {
if( list.isSelectionEmpty() ) {
list.addSelectionInterval( 1, 2 );
list.addSelectionInterval( 5, 5 );
}
}
private static void initSelection( JTree tree ) {
if( tree.isSelectionEmpty() ) {
tree.addSelectionInterval( 1, 2 );
tree.addSelectionInterval( 5, 5 );
}
}
private static void initSelection( JTable table ) {
if( table.getSelectedRowCount() == 0 ) {
table.addRowSelectionInterval( 1, 2 );
table.addRowSelectionInterval( 5, 5 );
}
}
private void dndChanged() {
boolean dnd = dndCheckBox.isSelected();
list1.setDragEnabled( dnd );
@@ -535,6 +597,12 @@ public class FlatComponents2Test
public void applyComponentOrientation( ComponentOrientation o ) {
super.applyComponentOrientation( o );
// always use left-to-right for options panels
generalOptionsPanel.applyComponentOrientation( ComponentOrientation.LEFT_TO_RIGHT );
listOptionsPanel.applyComponentOrientation( ComponentOrientation.LEFT_TO_RIGHT );
treeOptionsPanel.applyComponentOrientation( ComponentOrientation.LEFT_TO_RIGHT );
tableOptionsPanel.applyComponentOrientation( ComponentOrientation.LEFT_TO_RIGHT );
// 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 );
@@ -596,16 +664,22 @@ public class FlatComponents2Test
JLabel label2 = new JLabel();
xTreeTable1ScrollPane = new JScrollPane();
xTreeTable1 = new JXTreeTable();
JPanel panel5 = new JPanel();
generalOptionsPanel = new JPanel();
roundedSelectionCheckBox = new JCheckBox();
JLabel label6 = new JLabel();
topSelectionInsetsCheckBox = new JCheckBox();
bottomSelectionInsetsCheckBox = new JCheckBox();
leftSelectionInsetsCheckBox = new JCheckBox();
rightSelectionInsetsCheckBox = new JCheckBox();
dndCheckBox = new JCheckBox();
JPanel panel6 = new JPanel();
listOptionsPanel = new JPanel();
JLabel listRendererLabel = new JLabel();
listRendererComboBox = new JComboBox<>();
JLabel listLayoutOrientationLabel = new JLabel();
listLayoutOrientationField = new JComboBox<>();
JLabel listVisibleRowCountLabel = new JLabel();
listVisibleRowCountSpinner = new JSpinner();
JPanel treeOptionsPanel = new JPanel();
treeOptionsPanel = new JPanel();
JLabel treeRendererLabel = new JLabel();
treeRendererComboBox = new JComboBox<>();
treeWideSelectionCheckBox = new JCheckBox();
@@ -614,7 +688,7 @@ public class FlatComponents2Test
treeRedLinesCheckBox = new JCheckBox();
treeEditableCheckBox = new JCheckBox();
treeShowDefaultIconsCheckBox = new JCheckBox();
JPanel tableOptionsPanel = new JPanel();
tableOptionsPanel = new JPanel();
JLabel autoResizeModeLabel = new JLabel();
autoResizeModeField = new JComboBox<>();
JLabel sortIconPositionLabel = new JLabel();
@@ -872,30 +946,68 @@ public class FlatComponents2Test
}
add(xTreeTable1ScrollPane, "cell 4 3 2 1");
//======== panel5 ========
//======== generalOptionsPanel ========
{
panel5.setBorder(new TitledBorder("General Control"));
panel5.putClientProperty("FlatLaf.internal.testing.ignore", true);
panel5.setLayout(new MigLayout(
"hidemode 3",
generalOptionsPanel.setBorder(new TitledBorder("General Control"));
generalOptionsPanel.putClientProperty("FlatLaf.internal.testing.ignore", true);
generalOptionsPanel.setLayout(new MigLayout(
"insets 8,hidemode 3",
// columns
"[fill]",
"[left]",
// rows
"[]" +
"[]0" +
"[]0" +
"[]rel" +
"[]"));
//---- roundedSelectionCheckBox ----
roundedSelectionCheckBox.setText("rounded selection");
roundedSelectionCheckBox.setMnemonic('D');
roundedSelectionCheckBox.addActionListener(e -> roundedSelectionChanged());
generalOptionsPanel.add(roundedSelectionCheckBox, "cell 0 0");
//---- label6 ----
label6.setText("Selection insets:");
generalOptionsPanel.add(label6, "cell 0 1");
//---- topSelectionInsetsCheckBox ----
topSelectionInsetsCheckBox.setText("top");
topSelectionInsetsCheckBox.setMnemonic('D');
topSelectionInsetsCheckBox.addActionListener(e -> roundedSelectionChanged());
generalOptionsPanel.add(topSelectionInsetsCheckBox, "cell 0 2,gapx ind");
//---- bottomSelectionInsetsCheckBox ----
bottomSelectionInsetsCheckBox.setText("bottom");
bottomSelectionInsetsCheckBox.setMnemonic('D');
bottomSelectionInsetsCheckBox.addActionListener(e -> roundedSelectionChanged());
generalOptionsPanel.add(bottomSelectionInsetsCheckBox, "cell 0 2");
//---- leftSelectionInsetsCheckBox ----
leftSelectionInsetsCheckBox.setText("left");
leftSelectionInsetsCheckBox.setMnemonic('D');
leftSelectionInsetsCheckBox.addActionListener(e -> roundedSelectionChanged());
generalOptionsPanel.add(leftSelectionInsetsCheckBox, "cell 0 3,gapx ind");
//---- rightSelectionInsetsCheckBox ----
rightSelectionInsetsCheckBox.setText("right");
rightSelectionInsetsCheckBox.setMnemonic('D');
rightSelectionInsetsCheckBox.addActionListener(e -> roundedSelectionChanged());
generalOptionsPanel.add(rightSelectionInsetsCheckBox, "cell 0 3");
//---- dndCheckBox ----
dndCheckBox.setText("drag and drop");
dndCheckBox.setMnemonic('D');
dndCheckBox.addActionListener(e -> dndChanged());
panel5.add(dndCheckBox, "cell 0 0");
generalOptionsPanel.add(dndCheckBox, "cell 0 4");
}
add(panel5, "cell 0 4 4 1");
add(generalOptionsPanel, "cell 0 4 4 1");
//======== panel6 ========
//======== listOptionsPanel ========
{
panel6.setBorder(new TitledBorder("JList Control"));
panel6.setLayout(new MigLayout(
"hidemode 3",
listOptionsPanel.setBorder(new TitledBorder("JList Control"));
listOptionsPanel.setLayout(new MigLayout(
"insets 8,hidemode 3",
// columns
"[fill]" +
"[fill]",
@@ -906,7 +1018,7 @@ public class FlatComponents2Test
//---- listRendererLabel ----
listRendererLabel.setText("Renderer:");
panel6.add(listRendererLabel, "cell 0 0");
listOptionsPanel.add(listRendererLabel, "cell 0 0");
//---- listRendererComboBox ----
listRendererComboBox.setModel(new DefaultComboBoxModel<>(new String[] {
@@ -916,11 +1028,11 @@ public class FlatComponents2Test
"labelRounded"
}));
listRendererComboBox.addActionListener(e -> listRendererChanged());
panel6.add(listRendererComboBox, "cell 1 0");
listOptionsPanel.add(listRendererComboBox, "cell 1 0");
//---- listLayoutOrientationLabel ----
listLayoutOrientationLabel.setText("Orientation:");
panel6.add(listLayoutOrientationLabel, "cell 0 1");
listOptionsPanel.add(listLayoutOrientationLabel, "cell 0 1");
//---- listLayoutOrientationField ----
listLayoutOrientationField.setModel(new DefaultComboBoxModel<>(new String[] {
@@ -929,25 +1041,25 @@ public class FlatComponents2Test
"horzontal wrap"
}));
listLayoutOrientationField.addActionListener(e -> listLayoutOrientationChanged());
panel6.add(listLayoutOrientationField, "cell 1 1");
listOptionsPanel.add(listLayoutOrientationField, "cell 1 1");
//---- listVisibleRowCountLabel ----
listVisibleRowCountLabel.setText("Visible row count:");
panel6.add(listVisibleRowCountLabel, "cell 0 2");
listOptionsPanel.add(listVisibleRowCountLabel, "cell 0 2");
//---- listVisibleRowCountSpinner ----
listVisibleRowCountSpinner.setModel(new SpinnerNumberModel(8, 0, null, 1));
listVisibleRowCountSpinner.addChangeListener(e -> listVisibleRowCountChanged());
panel6.add(listVisibleRowCountSpinner, "cell 1 2");
listOptionsPanel.add(listVisibleRowCountSpinner, "cell 1 2");
}
add(panel6, "cell 0 4 4 1");
add(listOptionsPanel, "cell 0 4 4 1");
//======== treeOptionsPanel ========
{
treeOptionsPanel.setBorder(new TitledBorder("JTree Control"));
treeOptionsPanel.putClientProperty("FlatLaf.internal.testing.ignore", true);
treeOptionsPanel.setLayout(new MigLayout(
"hidemode 3",
"insets 8,hidemode 3",
// columns
"[left]",
// rows
@@ -1014,7 +1126,7 @@ public class FlatComponents2Test
tableOptionsPanel.setBorder(new TitledBorder("JTable Control"));
tableOptionsPanel.putClientProperty("FlatLaf.internal.testing.ignore", true);
tableOptionsPanel.setLayout(new MigLayout(
"hidemode 3",
"insets 8,hidemode 3",
// columns
"[]" +
"[fill]" +
@@ -1132,10 +1244,18 @@ public class FlatComponents2Test
private CheckBoxTree checkBoxTree1;
private JScrollPane xTreeTable1ScrollPane;
private JXTreeTable xTreeTable1;
private JPanel generalOptionsPanel;
private JCheckBox roundedSelectionCheckBox;
private JCheckBox topSelectionInsetsCheckBox;
private JCheckBox bottomSelectionInsetsCheckBox;
private JCheckBox leftSelectionInsetsCheckBox;
private JCheckBox rightSelectionInsetsCheckBox;
private JCheckBox dndCheckBox;
private JPanel listOptionsPanel;
private JComboBox<String> listRendererComboBox;
private JComboBox<String> listLayoutOrientationField;
private JSpinner listVisibleRowCountSpinner;
private JPanel treeOptionsPanel;
private JComboBox<String> treeRendererComboBox;
private JCheckBox treeWideSelectionCheckBox;
private JCheckBox treePaintSelectionCheckBox;
@@ -1143,6 +1263,7 @@ public class FlatComponents2Test
private JCheckBox treeRedLinesCheckBox;
private JCheckBox treeEditableCheckBox;
private JCheckBox treeShowDefaultIconsCheckBox;
private JPanel tableOptionsPanel;
private JComboBox<String> autoResizeModeField;
private JComboBox<String> sortIconPositionComboBox;
private JCheckBox showHorizontalLinesCheckBox;
@@ -1683,4 +1804,46 @@ public class FlatComponents2Test
return this;
}
}
//---- class TestLabelRoundedTableCellRenderer ----------------------------
@SuppressWarnings( "unused" )
private static class TestLabelRoundedTableCellRenderer
extends JLabel
implements TableCellRenderer
{
private JTable table;
private int row;
private int column;
private boolean isSelected;
TestLabelRoundedTableCellRenderer() {
setBorder( new FlatEmptyBorder( 1, 6, 1, 6 ) );
}
@Override
public Component getTableCellRendererComponent( JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column )
{
this.table = table;
this.row = row;
this.column = column;
this.isSelected = isSelected;
setText( String.valueOf( value ) );
setBackground( isSelected ? Color.green : table.getBackground() );
setForeground( isSelected ? Color.blue : table.getForeground() );
return this;
}
@Override
protected void paintComponent( Graphics g ) {
if( isSelected ) {
g.setColor( getBackground() );
FlatTableUI.paintCellSelection( table, g, row, column, 0, 0, getWidth(), getHeight() );
}
super.paintComponent( g );
}
}
}

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "8.0.0.0.194" Java: "17.0.2" encoding: "UTF-8"
JFDML JFormDesigner: "8.2.2.0.9999" Java: "21.0.1" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -297,13 +297,77 @@ new FormModel {
"value": "cell 4 3 2 1"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$columnConstraints": "[fill]"
"$rowConstraints": "[]"
"$layoutConstraints": "insets 8,hidemode 3"
"$columnConstraints": "[left]"
"$rowConstraints": "[][]0[]0[]rel[]"
} ) {
name: "panel5"
name: "generalOptionsPanel"
"border": new javax.swing.border.TitledBorder( "General Control" )
"$client.FlatLaf.internal.testing.ignore": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "roundedSelectionCheckBox"
"text": "rounded selection"
"mnemonic": 68
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.JLabel" ) {
name: "label6"
"text": "Selection insets:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "topSelectionInsetsCheckBox"
"text": "top"
"mnemonic": 68
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "roundedSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2,gapx ind"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "bottomSelectionInsetsCheckBox"
"text": "bottom"
"mnemonic": 68
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "roundedSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "leftSelectionInsetsCheckBox"
"text": "left"
"mnemonic": 68
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "roundedSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3,gapx ind"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "rightSelectionInsetsCheckBox"
"text": "right"
"mnemonic": 68
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "roundedSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "dndCheckBox"
"text": "drag and drop"
@@ -313,18 +377,21 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "dndChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
"value": "cell 0 4"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4 4 1"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$layoutConstraints": "insets 8,hidemode 3"
"$columnConstraints": "[fill][fill]"
"$rowConstraints": "[][][]"
} ) {
name: "panel6"
name: "listOptionsPanel"
"border": new javax.swing.border.TitledBorder( "JList Control" )
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormComponent( "javax.swing.JLabel" ) {
name: "listRendererLabel"
"text": "Renderer:"
@@ -392,13 +459,16 @@ new FormModel {
"value": "cell 0 4 4 1"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$layoutConstraints": "insets 8,hidemode 3"
"$columnConstraints": "[left]"
"$rowConstraints": "[][]0[]0[]0[]"
} ) {
name: "treeOptionsPanel"
"border": new javax.swing.border.TitledBorder( "JTree Control" )
"$client.FlatLaf.internal.testing.ignore": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormComponent( "javax.swing.JLabel" ) {
name: "treeRendererLabel"
"text": "Renderer:"
@@ -491,13 +561,16 @@ new FormModel {
"value": "cell 0 4 4 1"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$layoutConstraints": "insets 8,hidemode 3"
"$columnConstraints": "[][fill][fill]"
"$rowConstraints": "[][]0[]0[]0[]0"
} ) {
name: "tableOptionsPanel"
"border": new javax.swing.border.TitledBorder( "JTable Control" )
"$client.FlatLaf.internal.testing.ignore": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormComponent( "javax.swing.JLabel" ) {
name: "autoResizeModeLabel"
"text": "Auto resize mode:"