Skip to content

Commit

Permalink
Tree: better support for non-wide rounded selection
Browse files Browse the repository at this point in the history
  • Loading branch information
DevCharly committed Jun 3, 2022
1 parent 7bf1b26 commit 3802c64
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -353,9 +353,11 @@ protected void paintBackground( Graphics g ) {

/** @since 3 */
protected void paintSelection( Graphics g, Color selectionBackground, Insets selectionInsets, int selectionArc ) {
float arc = scale( selectionArc / 2f );

g.setColor( deriveBackground( selectionBackground ) );
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 */
Expand Down
54 changes: 38 additions & 16 deletions flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTreeUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package com.formdev.flatlaf.ui;

import static com.formdev.flatlaf.FlatClientProperties.*;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
Expand Down Expand Up @@ -377,7 +376,7 @@ public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
}

/**
* 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.
*/
@Override
Expand Down Expand Up @@ -458,7 +457,7 @@ protected void paintRow( Graphics g, Rectangle clipBounds, Insets insets, Rectan
paintWideSelection( g, clipBounds, insets, bounds, path, row, isExpanded, hasBeenExpanded, isLeaf );
} else {
// non-wide selection
paintCellBackground( g, rendererComponent, bounds );
paintCellBackground( g, rendererComponent, bounds, row, true );
}

// this is actually not necessary because renderer should always set color
Expand All @@ -472,7 +471,7 @@ protected void paintRow( Graphics g, Rectangle clipBounds, Insets insets, Rectan
if( bg != null && !bg.equals( defaultCellNonSelectionBackground ) ) {
Color oldColor = g.getColor();
g.setColor( bg );
paintCellBackground( g, rendererComponent, bounds );
paintCellBackground( g, rendererComponent, bounds, row, false );
g.setColor( oldColor );
}
}
Expand Down Expand Up @@ -527,16 +526,18 @@ private Color setRendererBorderSelectionColor( Component rendererComponent, Colo
private void paintWideSelection( Graphics g, Rectangle clipBounds, Insets insets, Rectangle bounds,
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( row > 0 && tree.isRowSelected( row - 1 ) )
flags |= FlatUIUtils.FLAG_TOP_NOT_ROUNDED;
arcTop = 0;
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,
UIScale.scale( selectionInsets ), UIScale.scale( (float) selectionArc ), flags );
UIScale.scale( selectionInsets ), arcTop, arcTop, arcBottom, arcBottom, 0 );

// paint expand/collapse icon
// (was already painted before, but painted over with wide selection)
Expand All @@ -546,12 +547,9 @@ private void paintWideSelection( Graphics g, Rectangle clipBounds, Insets insets
}
}

private boolean useUnitedRoundedSelection() {
return selectionArc > 0 &&
(selectionInsets == null || (selectionInsets.top == 0 && selectionInsets.bottom == 0));
}

private void paintCellBackground( Graphics g, Component rendererComponent, Rectangle bounds ) {
private void paintCellBackground( Graphics g, Component rendererComponent, Rectangle bounds,
int row, boolean paintSelection )
{
int xOffset = 0;
int imageOffset = 0;

Expand All @@ -564,8 +562,32 @@ private void paintCellBackground( Graphics g, Component rendererComponent, Recta
xOffset = label.getComponentOrientation().isLeftToRight() ? imageOffset : 0;
}

FlatUIUtils.paintSelection( (Graphics2D) g, bounds.x + xOffset, bounds.y, bounds.width - imageOffset, bounds.height,
UIScale.scale( selectionInsets ), UIScale.scale( (float) selectionArc ), 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,
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));
}

/**
Expand Down
27 changes: 5 additions & 22 deletions flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -624,26 +624,17 @@ static void paintFilledRectangle( Graphics g, Color color, float x, float y, flo
}
}

// 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.
* <p>
* 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}.
* 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
*/
public static void paintSelection( Graphics2D g, int x, int y, int width, int height,
Insets insets, float arc, int flags )
public static void paintSelection( Graphics2D g, int x, int y, int width, int height, Insets insets,
float arcTopLeft, float arcTopRight, float arcBottomLeft, float arcBottomRight, int flags )
{
if( insets != null ) {
x += insets.left;
Expand All @@ -652,15 +643,7 @@ public static void paintSelection( Graphics2D g, int x, int y, int width, int he
height -= insets.top + insets.bottom;
}

if( arc > 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;

if( arcTopLeft > 0 || arcTopRight > 0 || arcBottomLeft > 0 || arcBottomRight > 0 ) {
double systemScaleFactor = UIScale.getSystemScaleFactor( g );
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
Expand Down Expand Up @@ -765,7 +748,7 @@ public static Path2D createRectangle( float x, float y, float width, float heigh
}

/**
* 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,
float lineWidth, float arcTopLeft, float arcTopRight, float arcBottomLeft, float arcBottomRight )
Expand Down

0 comments on commit 3802c64

Please sign in to comment.