diff --git a/CHANGELOG.md b/CHANGELOG.md index 9761ec99..da8df871 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ FlatLaf Change Log ================== +## 3.7-SNAPSHOT + +#### Fixed bugs + +- Tree and List: Fixed painting of rounded drop backgrounds. (issue #1023) + + ## 3.6.1 - Extras: Support JSVG 2.0.0. Minimum JSVG version is now 1.6.0. (issue #997) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatListUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatListUI.java index 59e814e6..e285add3 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatListUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatListUI.java @@ -302,6 +302,7 @@ public class FlatListUI ListModel dataModel, ListSelectionModel selModel, int leadIndex ) { boolean isSelected = selModel.isSelectedIndex( row ); + boolean isDropRow = isDropRow( row ); // paint alternating rows if( alternateRowColor != null && row % 2 != 0 && @@ -335,7 +336,7 @@ public class FlatListUI } // rounded selection or selection insets - if( isSelected && + if( (isSelected || isDropRow) && !isFileList && // rounded selection is not supported for file list (rendererComponent instanceof DefaultListCellRenderer || rendererComponent instanceof BasicComboBoxRenderer) && @@ -376,7 +377,22 @@ public class FlatListUI this.getColor() == rendererComponent.getBackground() ) { inPaintSelection = true; - paintCellSelection( this, row, x, y, width, height ); + if( isDropRow ) { + // for rounded drop background, it is necessary to first + // paint selection background because may be not rounded on some corners + if( isSelected ) { + Color oldColor = getColor(); + setColor( list.getSelectionBackground() ); + paintCellSelection( this, row, x, y, width, height ); + setColor( oldColor ); + } + + // paint drop background + float arc = UIScale.scale( selectionArc / 2f ); + FlatUIUtils.paintSelection( this, x, y, width, height, + UIScale.scale( selectionInsets ), arc, arc, arc, arc, 0 ); + } else + paintCellSelection( this, row, x, y, width, height ); inPaintSelection = false; } else super.fillRect( x, y, width, height ); @@ -475,4 +491,15 @@ public class FlatListUI FlatListUI ui = (FlatListUI) list.getUI(); ui.paintCellSelection( g, row, x, y, width, height ); } + + /** + * Checks whether dropping on a row. + * See DefaultListCellRenderer.getListCellRendererComponent(). + */ + private boolean isDropRow( int row ) { + JList.DropLocation dropLocation = list.getDropLocation(); + return dropLocation != null && + !dropLocation.isInsert() && + dropLocation.getIndex() == row; + } } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTreeUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTreeUI.java index 2007bc22..9e2e1da3 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTreeUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTreeUI.java @@ -577,7 +577,6 @@ public class FlatTreeUI boolean isEditing = (editingComponent != null && editingRow == row); boolean isSelected = tree.isRowSelected( row ); boolean isDropRow = isDropRow( row ); - boolean needsSelectionPainting = (isSelected || isDropRow) && isPaintSelection(); // paint alternating rows if( alternateRowColor != null && row % 2 != 0 ) { @@ -608,7 +607,7 @@ public class FlatTreeUI if( isSelected && isWideSelection() ) { Color oldColor = g.getColor(); g.setColor( selectionInactiveBackground ); - paintWideSelection( g, bounds, row ); + paintWideSelection( g, bounds, row, false ); g.setColor( oldColor ); } return; @@ -628,7 +627,7 @@ public class FlatTreeUI // renderer background/foreground Color oldBackgroundSelectionColor = null; - if( isSelected && !hasFocus && !isDropRow ) { + if( isSelected && !hasFocus ) { // apply inactive selection background/foreground if tree is not focused oldBackgroundSelectionColor = setRendererBackgroundSelectionColor( rendererComponent, selectionInactiveBackground ); setRendererForeground( rendererComponent, selectionInactiveForeground ); @@ -655,26 +654,12 @@ public class FlatTreeUI } // paint selection background - if( needsSelectionPainting ) { - // set selection color - Color oldColor = g.getColor(); - g.setColor( isDropRow - ? UIManager.getColor( "Tree.dropCellBackground" ) - : (rendererComponent instanceof DefaultTreeCellRenderer - ? ((DefaultTreeCellRenderer)rendererComponent).getBackgroundSelectionColor() - : (hasFocus ? selectionBackground : selectionInactiveBackground)) ); + if( isSelected && isPaintSelection() ) { + Color selectionColor = rendererComponent instanceof DefaultTreeCellRenderer + ? ((DefaultTreeCellRenderer)rendererComponent).getBackgroundSelectionColor() + : (hasFocus ? selectionBackground : selectionInactiveBackground); - if( isWideSelection() ) { - // wide selection - paintWideSelection( g, bounds, row ); - } else { - // non-wide selection - paintCellBackground( g, rendererComponent, bounds, row, true ); - } - - // this is actually not necessary because renderer should always set color - // before painting, but doing anyway to avoid any side effect (in bad renderers) - g.setColor( oldColor ); + paintRowSelection( g, selectionColor, rendererComponent, bounds, row, hasFocus, false ); } else { // paint cell background if DefaultTreeCellRenderer.getBackgroundNonSelectionColor() is set if( rendererComponent instanceof DefaultTreeCellRenderer ) { @@ -683,12 +668,19 @@ public class FlatTreeUI if( bg != null && !bg.equals( defaultCellNonSelectionBackground ) ) { Color oldColor = g.getColor(); g.setColor( bg ); - paintCellBackground( g, rendererComponent, bounds, row, false ); + paintCellBackground( g, rendererComponent, bounds, row, false, false ); g.setColor( oldColor ); } } } + // paint drop background + // (this needs to be an extra step for rounded selection) + if( isDropRow && isPaintSelection() ) { + paintRowSelection( g, UIManager.getColor( "Tree.dropCellBackground" ), + rendererComponent, bounds, row, hasFocus, true ); + } + // paint renderer rendererPane.paintComponent( g, rendererComponent, tree, bounds.x, bounds.y, bounds.width, bounds.height, true ); @@ -699,6 +691,26 @@ public class FlatTreeUI ((DefaultTreeCellRenderer)rendererComponent).setBorderSelectionColor( oldBorderSelectionColor ); } + private void paintRowSelection( Graphics g, Color color, Component rendererComponent, + Rectangle bounds, int row, boolean hasFocus, boolean paintDropSelection ) + { + // set selection color + Color oldColor = g.getColor(); + g.setColor( color ); + + if( isWideSelection() ) { + // wide selection + paintWideSelection( g, bounds, row, paintDropSelection ); + } else { + // non-wide selection + paintCellBackground( g, rendererComponent, bounds, row, true, paintDropSelection ); + } + + // this is actually not necessary because renderer should always set color + // before painting, but doing anyway to avoid any side effect (in bad renderers) + g.setColor( oldColor ); + } + private Color setRendererBackgroundSelectionColor( Component rendererComponent, Color color ) { Color oldColor = null; @@ -735,11 +747,11 @@ public class FlatTreeUI return oldColor; } - private void paintWideSelection( Graphics g, Rectangle bounds, int row ) { + private void paintWideSelection( Graphics g, Rectangle bounds, int row, boolean paintDropSelection ) { float arcTop, arcBottom; arcTop = arcBottom = UIScale.scale( selectionArc / 2f ); - if( useUnitedRoundedSelection() ) { + if( useUnitedRoundedSelection() && !paintDropSelection ) { if( row > 0 && tree.isRowSelected( row - 1 ) ) arcTop = 0; if( row < tree.getRowCount() - 1 && tree.isRowSelected( row + 1 ) ) @@ -751,7 +763,7 @@ public class FlatTreeUI } private void paintCellBackground( Graphics g, Component rendererComponent, Rectangle bounds, - int row, boolean paintSelection ) + int row, boolean paintSelection, boolean paintDropSelection ) { int xOffset = 0; int imageOffset = 0; @@ -769,7 +781,7 @@ public class FlatTreeUI float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight; arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f ); - if( useUnitedRoundedSelection() ) { + if( useUnitedRoundedSelection() && !paintDropSelection ) { if( row > 0 && tree.isRowSelected( row - 1 ) ) { Rectangle r = getPathBounds( tree, tree.getPathForRow( row - 1 ) ); arcTopLeft = Math.min( arcTopLeft, r.x - bounds.x ); 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 e5a2502f..36b54697 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 @@ -162,6 +162,11 @@ public class FlatComponents2Test for( JTree tree : allTrees ) expandTree( tree ); + + // drop mode + dropModeComboBox.init( DropMode.class, false ); + dropModeComboBox.setSelectedItem( DropMode.ON_OR_INSERT ); + dropModeChanged(); } private void initTableEditors( JTable table ) { @@ -311,13 +316,6 @@ public class FlatComponents2Test xTable1.setDragEnabled( dnd ); xTreeTable1.setDragEnabled( dnd ); - DropMode dropMode = dnd ? DropMode.ON_OR_INSERT : DropMode.USE_SELECTION; - list1.setDropMode( dropMode ); - tree1.setDropMode( dropMode ); - table1.setDropMode( dropMode ); - xTable1.setDropMode( dropMode ); - xTreeTable1.setDropMode( dropMode ); - String key = "FlatLaf.oldTransferHandler"; if( dnd ) { list1.putClientProperty( key, list1.getTransferHandler() ); @@ -341,6 +339,32 @@ public class FlatComponents2Test } } + private void dropModeChanged() { + DropMode dropMode = dropModeComboBox.getSelectedValue(); + DropMode dropMode2; + switch( dropMode ) { + case INSERT_ROWS: + case INSERT_COLS: + dropMode2 = DropMode.INSERT; + break; + + case ON_OR_INSERT_ROWS: + case ON_OR_INSERT_COLS: + dropMode2 = DropMode.ON_OR_INSERT; + break; + + default: + dropMode2 = dropMode; + break; + } + + list1.setDropMode( dropMode2 ); + tree1.setDropMode( dropMode2 ); + table1.setDropMode( dropMode ); + xTable1.setDropMode( dropMode ); + xTreeTable1.setDropMode( dropMode ); + } + private void tableHeaderButtonChanged() { tableHeaderButtonChanged( table1ScrollPane ); tableHeaderButtonChanged( xTable1ScrollPane ); @@ -618,6 +642,7 @@ public class FlatComponents2Test super.updateUI(); EventQueue.invokeLater( () -> { + dropModeChanged(); showHorizontalLinesChanged(); showVerticalLinesChanged(); intercellSpacingChanged(); @@ -673,6 +698,7 @@ public class FlatComponents2Test leftSelectionInsetsCheckBox = new JCheckBox(); rightSelectionInsetsCheckBox = new JCheckBox(); dndCheckBox = new JCheckBox(); + dropModeComboBox = new FlatTestEnumComboBox<>(); listOptionsPanel = new JPanel(); JLabel listRendererLabel = new JLabel(); listRendererComboBox = new JComboBox<>(); @@ -963,6 +989,7 @@ public class FlatComponents2Test "[]0" + "[]0" + "[]rel" + + "[]" + "[]")); //---- roundedSelectionCheckBox ---- @@ -1004,6 +1031,10 @@ public class FlatComponents2Test dndCheckBox.setMnemonic('D'); dndCheckBox.addActionListener(e -> dndChanged()); generalOptionsPanel.add(dndCheckBox, "cell 0 4"); + + //---- dropModeComboBox ---- + dropModeComboBox.addActionListener(e -> dropModeChanged()); + generalOptionsPanel.add(dropModeComboBox, "cell 0 5"); } add(generalOptionsPanel, "cell 0 4 4 1"); @@ -1272,6 +1303,7 @@ public class FlatComponents2Test private JCheckBox leftSelectionInsetsCheckBox; private JCheckBox rightSelectionInsetsCheckBox; private JCheckBox dndCheckBox; + private FlatTestEnumComboBox dropModeComboBox; private JPanel listOptionsPanel; private JComboBox listRendererComboBox; private JComboBox listLayoutOrientationField; 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 d8fd15a5..81200ac4 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 @@ -299,7 +299,7 @@ new FormModel { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { "$layoutConstraints": "insets 8,hidemode 3" "$columnConstraints": "[left]" - "$rowConstraints": "[][]0[]0[]rel[]" + "$rowConstraints": "[][]0[]0[]rel[][]" } ) { name: "generalOptionsPanel" "border": new javax.swing.border.TitledBorder( "General Control" ) @@ -379,6 +379,16 @@ new FormModel { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 0 4" } ) + add( new FormComponent( "com.formdev.flatlaf.testing.FlatTestEnumComboBox" ) { + name: "dropModeComboBox" + auxiliary() { + "JavaCodeGenerator.variableLocal": false + "JavaCodeGenerator.typeParameters": "DropMode" + } + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "dropModeChanged", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 5" + } ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 0 4 4 1" } )