Tree: better support for non-wide rounded selection

This commit is contained in:
Karl Tauber
2022-06-03 09:33:19 +02:00
parent 7bf1b26812
commit 3802c64be3
3 changed files with 46 additions and 39 deletions

View File

@@ -353,9 +353,11 @@ debug*/
/** @since 3 */ /** @since 3 */
protected void paintSelection( Graphics g, Color selectionBackground, Insets selectionInsets, int selectionArc ) { protected void paintSelection( Graphics g, Color selectionBackground, Insets selectionInsets, int selectionArc ) {
float arc = scale( selectionArc / 2f );
g.setColor( deriveBackground( selectionBackground ) ); g.setColor( deriveBackground( selectionBackground ) );
FlatUIUtils.paintSelection( (Graphics2D) g, 0, 0, menuItem.getWidth(), menuItem.getHeight(), FlatUIUtils.paintSelection( (Graphics2D) g, 0, 0, menuItem.getWidth(), menuItem.getHeight(),
scale( selectionInsets ), scale( (float) selectionArc ), 0 ); scale( selectionInsets ), arc, arc, arc, arc, 0 );
} }
/** @since 3 */ /** @since 3 */

View File

@@ -17,7 +17,6 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.FlatClientProperties.*; import static com.formdev.flatlaf.FlatClientProperties.*;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics; import java.awt.Graphics;
@@ -377,7 +376,7 @@ public class FlatTreeUI
} }
/** /**
* Same as super.paintRow(), but supports wide selection and uses * Similar to super.paintRow(), but supports wide selection and uses
* inactive selection background/foreground if tree is not focused. * inactive selection background/foreground if tree is not focused.
*/ */
@Override @Override
@@ -458,7 +457,7 @@ public class FlatTreeUI
paintWideSelection( g, clipBounds, insets, bounds, path, row, isExpanded, hasBeenExpanded, isLeaf ); paintWideSelection( g, clipBounds, insets, bounds, path, row, isExpanded, hasBeenExpanded, isLeaf );
} else { } else {
// non-wide selection // non-wide selection
paintCellBackground( g, rendererComponent, bounds ); paintCellBackground( g, rendererComponent, bounds, row, true );
} }
// this is actually not necessary because renderer should always set color // this is actually not necessary because renderer should always set color
@@ -472,7 +471,7 @@ public class FlatTreeUI
if( bg != null && !bg.equals( defaultCellNonSelectionBackground ) ) { if( bg != null && !bg.equals( defaultCellNonSelectionBackground ) ) {
Color oldColor = g.getColor(); Color oldColor = g.getColor();
g.setColor( bg ); g.setColor( bg );
paintCellBackground( g, rendererComponent, bounds ); paintCellBackground( g, rendererComponent, bounds, row, false );
g.setColor( oldColor ); g.setColor( oldColor );
} }
} }
@@ -527,16 +526,18 @@ public class FlatTreeUI
private void paintWideSelection( Graphics g, Rectangle clipBounds, Insets insets, Rectangle bounds, private void paintWideSelection( Graphics g, Rectangle clipBounds, Insets insets, Rectangle bounds,
TreePath path, int row, boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf ) TreePath path, int row, boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf )
{ {
int flags = 0; float arcTop, arcBottom;
arcTop = arcBottom = UIScale.scale( selectionArc / 2f );
if( useUnitedRoundedSelection() ) { if( useUnitedRoundedSelection() ) {
if( row > 0 && tree.isRowSelected( row - 1 ) ) if( row > 0 && tree.isRowSelected( row - 1 ) )
flags |= FlatUIUtils.FLAG_TOP_NOT_ROUNDED; arcTop = 0;
if( row < tree.getRowCount() - 1 && tree.isRowSelected( row + 1 ) ) if( row < tree.getRowCount() - 1 && tree.isRowSelected( row + 1 ) )
flags |= FlatUIUtils.FLAG_BOTTOM_NOT_ROUNDED; arcBottom = 0;
} }
FlatUIUtils.paintSelection( (Graphics2D) g, 0, bounds.y, tree.getWidth(), bounds.height, FlatUIUtils.paintSelection( (Graphics2D) g, 0, bounds.y, tree.getWidth(), bounds.height,
UIScale.scale( selectionInsets ), UIScale.scale( (float) selectionArc ), flags ); UIScale.scale( selectionInsets ), arcTop, arcTop, arcBottom, arcBottom, 0 );
// paint expand/collapse icon // paint expand/collapse icon
// (was already painted before, but painted over with wide selection) // (was already painted before, but painted over with wide selection)
@@ -546,12 +547,9 @@ public class FlatTreeUI
} }
} }
private boolean useUnitedRoundedSelection() { private void paintCellBackground( Graphics g, Component rendererComponent, Rectangle bounds,
return selectionArc > 0 && int row, boolean paintSelection )
(selectionInsets == null || (selectionInsets.top == 0 && selectionInsets.bottom == 0)); {
}
private void paintCellBackground( Graphics g, Component rendererComponent, Rectangle bounds ) {
int xOffset = 0; int xOffset = 0;
int imageOffset = 0; int imageOffset = 0;
@@ -564,8 +562,32 @@ public class FlatTreeUI
xOffset = label.getComponentOrientation().isLeftToRight() ? imageOffset : 0; xOffset = label.getComponentOrientation().isLeftToRight() ? imageOffset : 0;
} }
if( paintSelection ) {
float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight;
arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f );
if( useUnitedRoundedSelection() ) {
if( row > 0 && tree.isRowSelected( row - 1 ) ) {
Rectangle r = getPathBounds( tree, tree.getPathForRow( row - 1 ) );
arcTopLeft = Math.min( arcTopLeft, r.x - bounds.x );
arcTopRight = Math.min( arcTopRight, (bounds.x + bounds.width) - (r.x + r.width) );
}
if( row < tree.getRowCount() - 1 && tree.isRowSelected( row + 1 ) ) {
Rectangle r = getPathBounds( tree, tree.getPathForRow( row + 1 ) );
arcBottomLeft = Math.min( arcBottomLeft, r.x - bounds.x );
arcBottomRight = Math.min( arcBottomRight, (bounds.x + bounds.width) - (r.x + r.width) );
}
}
FlatUIUtils.paintSelection( (Graphics2D) g, bounds.x + xOffset, bounds.y, bounds.width - imageOffset, bounds.height, FlatUIUtils.paintSelection( (Graphics2D) g, bounds.x + xOffset, bounds.y, bounds.width - imageOffset, bounds.height,
UIScale.scale( selectionInsets ), UIScale.scale( (float) selectionArc ), 0 ); UIScale.scale( selectionInsets ), arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight, 0 );
} else
g.fillRect( bounds.x + xOffset, bounds.y, bounds.width - imageOffset, bounds.height );
}
private boolean useUnitedRoundedSelection() {
return selectionArc > 0 &&
(selectionInsets == null || (selectionInsets.top == 0 && selectionInsets.bottom == 0));
} }
/** /**

View File

@@ -624,26 +624,17 @@ public class FlatUIUtils
} }
} }
// flags for paintSelection()
public static final int
FLAG_TOP_LEFT_NOT_ROUNDED = (1 << 0),
FLAG_TOP_RIGHT_NOT_ROUNDED = (1 << 1),
FLAG_BOTTOM_LEFT_NOT_ROUNDED = (1 << 2),
FLAG_BOTTOM_RIGHT_NOT_ROUNDED = (1 << 3),
FLAG_TOP_NOT_ROUNDED = FLAG_TOP_LEFT_NOT_ROUNDED | FLAG_TOP_RIGHT_NOT_ROUNDED,
FLAG_BOTTOM_NOT_ROUNDED = FLAG_BOTTOM_LEFT_NOT_ROUNDED | FLAG_BOTTOM_RIGHT_NOT_ROUNDED;
/** /**
* Paints a selection. * Paints a selection.
* <p> * <p>
* The bounds of the painted selection (rounded) rectangle are * The bounds of the painted selection (rounded) rectangle are
* {@code x + insets.left, y + insets.top, width - insets.left - insets.right, height - insets.top - insets.bottom}. * {@code x + insets.left, y + insets.top, width - insets.left - insets.right, height - insets.top - insets.bottom}.
* The given arc diameter refers to the painted rectangle (and not to {@code x,y,width,height}). * The given arc radius refers to the painted rectangle (and not to {@code x,y,width,height}).
* *
* @since 3 * @since 3
*/ */
public static void paintSelection( Graphics2D g, int x, int y, int width, int height, public static void paintSelection( Graphics2D g, int x, int y, int width, int height, Insets insets,
Insets insets, float arc, int flags ) float arcTopLeft, float arcTopRight, float arcBottomLeft, float arcBottomRight, int flags )
{ {
if( insets != null ) { if( insets != null ) {
x += insets.left; x += insets.left;
@@ -652,15 +643,7 @@ public class FlatUIUtils
height -= insets.top + insets.bottom; height -= insets.top + insets.bottom;
} }
if( arc > 0 ) { if( arcTopLeft > 0 || arcTopRight > 0 || arcBottomLeft > 0 || arcBottomRight > 0 ) {
// because createRoundRectanglePath() expects a radius
float arcRadius = arc / 2;
float arcTopLeft = ((flags & FLAG_TOP_LEFT_NOT_ROUNDED) != 0) ? 0 : arcRadius;
float arcTopRight = ((flags & FLAG_TOP_RIGHT_NOT_ROUNDED) != 0) ? 0 : arcRadius;
float arcBottomLeft = ((flags & FLAG_BOTTOM_LEFT_NOT_ROUNDED) != 0) ? 0 : arcRadius;
float arcBottomRight = ((flags & FLAG_BOTTOM_RIGHT_NOT_ROUNDED) != 0) ? 0 : arcRadius;
double systemScaleFactor = UIScale.getSystemScaleFactor( g ); double systemScaleFactor = UIScale.getSystemScaleFactor( g );
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) { if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175% // paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
@@ -765,7 +748,7 @@ public class FlatUIUtils
} }
/** /**
* Creates a not-filled rounded rectangle shape and allows specifying the line width and the radius or each corner. * Creates a not-filled rounded rectangle shape and allows specifying the line width and the radius of each corner.
*/ */
public static Path2D createRoundRectangle( float x, float y, float width, float height, public static Path2D createRoundRectangle( float x, float y, float width, float height,
float lineWidth, float arcTopLeft, float arcTopRight, float arcBottomLeft, float arcBottomRight ) float lineWidth, float arcTopLeft, float arcTopRight, float arcBottomLeft, float arcBottomRight )