Tree and List: fixed painting of rounded drop backgrounds (issue #1023)

This commit is contained in:
Karl Tauber
2025-09-06 00:15:11 +02:00
parent e7a766bf8f
commit d388158de7
5 changed files with 125 additions and 37 deletions

View File

@@ -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)

View File

@@ -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,6 +377,21 @@ public class FlatListUI
this.getColor() == rendererComponent.getBackground() )
{
inPaintSelection = true;
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
@@ -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;
}
}

View File

@@ -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
if( isSelected && isPaintSelection() ) {
Color selectionColor = rendererComponent instanceof DefaultTreeCellRenderer
? ((DefaultTreeCellRenderer)rendererComponent).getBackgroundSelectionColor()
: (hasFocus ? selectionBackground : selectionInactiveBackground)) );
: (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 );

View File

@@ -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<DropMode> dropModeComboBox;
private JPanel listOptionsPanel;
private JComboBox<String> listRendererComboBox;
private JComboBox<String> listLayoutOrientationField;

View File

@@ -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"
} )