From 7bf1b26812db486a4380a745dcf5010e39a8e08e Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Thu, 2 Jun 2022 12:03:20 +0200 Subject: [PATCH 1/2] Tree: support rounded selection --- .../flatlaf/ui/FlatMenuItemRenderer.java | 10 +-- .../com/formdev/flatlaf/ui/FlatTreeUI.java | 55 ++++++++++++++- .../com/formdev/flatlaf/ui/FlatUIUtils.java | 67 ++++++++++++++++++- .../com/formdev/flatlaf/FlatLaf.properties | 2 + .../flatlaf/ui/TestFlatStyleableInfo.java | 2 + .../formdev/flatlaf/ui/TestFlatStyling.java | 2 + .../dumps/uidefaults/FlatDarkLaf_1.8.0.txt | 2 + .../dumps/uidefaults/FlatLightLaf_1.8.0.txt | 2 + .../dumps/uidefaults/FlatTestLaf_1.8.0.txt | 2 + .../flatlaf/themeeditor/FlatLafUIKeys.txt | 2 + 10 files changed, 133 insertions(+), 13 deletions(-) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemRenderer.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemRenderer.java index bdd093a58..60f0e1b27 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemRenderer.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemRenderer.java @@ -353,15 +353,9 @@ protected void paintBackground( Graphics g ) { /** @since 3 */ protected void paintSelection( Graphics g, Color selectionBackground, Insets selectionInsets, int selectionArc ) { - Rectangle r = FlatUIUtils.subtractInsets( new Rectangle( menuItem.getSize() ), scale( selectionInsets ) ); - g.setColor( deriveBackground( selectionBackground ) ); - if( selectionArc > 0 ) { - Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g ); - FlatUIUtils.paintComponentBackground( (Graphics2D) g, r.x, r.y, r.width, r.height, 0, scale( selectionArc ) ); - FlatUIUtils.resetRenderingHints( g, oldRenderingHints ); - } else - g.fillRect( r.x, r.y, r.width, r.height ); + FlatUIUtils.paintSelection( (Graphics2D) g, 0, 0, menuItem.getWidth(), menuItem.getHeight(), + scale( selectionInsets ), scale( (float) selectionArc ), 0 ); } /** @since 3 */ 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 ee610c2dc..20b6bc898 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 @@ -21,6 +21,7 @@ import java.awt.Color; import java.awt.Component; import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.Insets; import java.awt.Rectangle; import java.awt.event.MouseEvent; @@ -36,6 +37,7 @@ import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.JTree.DropLocation; +import javax.swing.event.TreeSelectionListener; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicTreeUI; import javax.swing.tree.DefaultTreeCellRenderer; @@ -96,6 +98,8 @@ * @uiDefault Tree.selectionForeground Color * @uiDefault Tree.selectionInactiveBackground Color * @uiDefault Tree.selectionInactiveForeground Color + * @uiDefault Tree.selectionInsets Insets + * @uiDefault Tree.selectionArc int * @uiDefault Tree.wideSelection boolean * @uiDefault Tree.showCellFocusIndicator boolean * @@ -132,6 +136,8 @@ public class FlatTreeUI @Styleable protected Color selectionInactiveBackground; @Styleable protected Color selectionInactiveForeground; @Styleable protected Color selectionBorderColor; + /** @since 3 */ @Styleable protected Insets selectionInsets; + /** @since 3 */ @Styleable protected int selectionArc; @Styleable protected boolean wideSelection; @Styleable protected boolean showCellFocusIndicator; @@ -175,6 +181,8 @@ protected void installDefaults() { selectionInactiveBackground = UIManager.getColor( "Tree.selectionInactiveBackground" ); selectionInactiveForeground = UIManager.getColor( "Tree.selectionInactiveForeground" ); selectionBorderColor = UIManager.getColor( "Tree.selectionBorderColor" ); + selectionInsets = UIManager.getInsets( "Tree.selectionInsets" ); + selectionArc = UIManager.getInt( "Tree.selectionArc" ); wideSelection = UIManager.getBoolean( "Tree.wideSelection" ); showCellFocusIndicator = UIManager.getBoolean( "Tree.showCellFocusIndicator" ); @@ -295,6 +303,34 @@ private void repaintWideDropLocation(JTree.DropLocation loc) { tree.repaint( 0, r.y, tree.getWidth(), r.height ); } + @Override + protected TreeSelectionListener createTreeSelectionListener() { + TreeSelectionListener superListener = super.createTreeSelectionListener(); + return e -> { + superListener.valueChanged( e ); + + // for united rounded selection, repaint parts of the rows that adjoin to the changed rows + TreePath[] changedPaths; + if( useUnitedRoundedSelection() && + tree.getSelectionCount() > 1 && + (changedPaths = e.getPaths()) != null ) + { + if( changedPaths.length > 4 ) { + // same is done in BasicTreeUI.Handler.valueChanged() + tree.repaint(); + } else { + int arcHeight = (int) Math.ceil( UIScale.scale( (float) selectionArc ) ); + + for( TreePath path : changedPaths ) { + Rectangle r = getPathBounds( tree, path ); + if( r != null ) + tree.repaint( r.x, r.y - arcHeight, r.width, r.height + (arcHeight * 2) ); + } + } + } + }; + } + @Override public Rectangle getPathBounds( JTree tree, TreePath path ) { Rectangle bounds = super.getPathBounds( tree, path ); @@ -491,7 +527,16 @@ 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 ) { - g.fillRect( 0, bounds.y, tree.getWidth(), bounds.height ); + int flags = 0; + if( useUnitedRoundedSelection() ) { + if( row > 0 && tree.isRowSelected( row - 1 ) ) + flags |= FlatUIUtils.FLAG_TOP_NOT_ROUNDED; + if( row < tree.getRowCount() - 1 && tree.isRowSelected( row + 1 ) ) + flags |= FlatUIUtils.FLAG_BOTTOM_NOT_ROUNDED; + } + + FlatUIUtils.paintSelection( (Graphics2D) g, 0, bounds.y, tree.getWidth(), bounds.height, + UIScale.scale( selectionInsets ), UIScale.scale( (float) selectionArc ), flags ); // paint expand/collapse icon // (was already painted before, but painted over with wide selection) @@ -501,6 +546,11 @@ 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 ) { int xOffset = 0; int imageOffset = 0; @@ -514,7 +564,8 @@ private void paintCellBackground( Graphics g, Component rendererComponent, Recta xOffset = label.getComponentOrientation().isLeftToRight() ? imageOffset : 0; } - g.fillRect( 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 ); } /** diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java index fd3ddea23..fa4abe0c3 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java @@ -393,9 +393,9 @@ public static Color deriveColor( Color color, Color baseColor ) { } /** - * Fills the background of a component with a round rectangle. + * Fills the background of a component with a rounded rectangle. *

- * The bounds of the painted round rectangle are + * The bounds of the painted rounded rectangle are * {@code x + focusWidth, y + focusWidth, width - (focusWidth * 2), height - (focusWidth * 2)}. * The given arc diameter refers to the painted rectangle (and not to {@code x,y,width,height}). * @@ -426,7 +426,7 @@ public static void paintComponentBackground( Graphics2D g, int x, int y, int wid *

* * Background: - * The bounds of the filled round rectangle are + * The bounds of the filled rounded rectangle are * {@code [x + focusWidth, y + focusWidth, width - (focusWidth * 2), height - (focusWidth * 2)]}. * The focus border and the border may paint over the background. *

@@ -624,6 +624,67 @@ 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. + *

+ * 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}). + * + * @since 3 + */ + public static void paintSelection( Graphics2D g, int x, int y, int width, int height, + Insets insets, float arc, int flags ) + { + if( insets != null ) { + x += insets.left; + y += insets.top; + width -= insets.left + insets.right; + 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; + + 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% + HiDPIUtils.paintAtScale1x( g, x, y, width, height, + (g2d, x2, y2, width2, height2, scaleFactor) -> { + paintRoundedSelectionImpl( g2d, x2, y2, width2, height2, + (float) (arcTopLeft * scaleFactor), (float) (arcTopRight * scaleFactor), + (float) (arcBottomLeft * scaleFactor), (float) (arcBottomRight * scaleFactor) ); + } ); + } else + paintRoundedSelectionImpl( g, x, y, width, height, arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight ); + + } else + g.fillRect( x, y, width, height ); + } + + private static void paintRoundedSelectionImpl( Graphics2D g, int x, int y, int width, int height, + float arcTopLeft, float arcTopRight, float arcBottomLeft, float arcBottomRight ) + { + Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g ); + g.fill( FlatUIUtils.createRoundRectanglePath( x, y, width, height, arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight ) ); + FlatUIUtils.resetRenderingHints( g, oldRenderingHints ); + } + public static void paintGrip( Graphics g, int x, int y, int width, int height, boolean horizontal, int dotCount, int dotSize, int gap, boolean centerPrecise ) { diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties index 807f3df3f..fa8590a37 100644 --- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties +++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties @@ -881,6 +881,8 @@ Tree.dropCellForeground = @dropCellForeground Tree.dropLineColor = @dropLineColor Tree.rendererFillBackground = false Tree.rendererMargins = 1,2,1,2 +Tree.selectionInsets = 0,0,0,0 +Tree.selectionArc = 0 Tree.wideSelection = true Tree.repaintWholeRow = true Tree.paintLines = false diff --git a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableInfo.java b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableInfo.java index f577dde7c..2fa1990ee 100644 --- a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableInfo.java +++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableInfo.java @@ -921,6 +921,8 @@ void tree() { "selectionInactiveBackground", Color.class, "selectionInactiveForeground", Color.class, "selectionBorderColor", Color.class, + "selectionInsets", Insets.class, + "selectionArc", int.class, "wideSelection", boolean.class, "showCellFocusIndicator", boolean.class, diff --git a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyling.java b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyling.java index 51c107dbb..06c964973 100644 --- a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyling.java +++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyling.java @@ -1127,6 +1127,8 @@ void tree() { ui.applyStyle( "selectionInactiveBackground: #fff" ); ui.applyStyle( "selectionInactiveForeground: #fff" ); ui.applyStyle( "selectionBorderColor: #fff" ); + ui.applyStyle( "selectionInsets: 1,2,3,4" ); + ui.applyStyle( "selectionArc: 8" ); ui.applyStyle( "wideSelection: true" ); ui.applyStyle( "showCellFocusIndicator: true" ); diff --git a/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt b/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt index 7f0612117..b3a6d69a8 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt @@ -1376,11 +1376,13 @@ Tree.repaintWholeRow true Tree.rightChildIndent 11 Tree.rowHeight 0 Tree.scrollsOnExpand true +Tree.selectionArc 0 Tree.selectionBackground #4b6eaf HSL 219 40 49 javax.swing.plaf.ColorUIResource [UI] Tree.selectionBorderColor #000000 HSL 0 0 0 javax.swing.plaf.ColorUIResource [UI] Tree.selectionForeground #bbbbbb HSL 0 0 73 javax.swing.plaf.ColorUIResource [UI] Tree.selectionInactiveBackground #0f2a3d HSL 205 61 15 javax.swing.plaf.ColorUIResource [UI] Tree.selectionInactiveForeground #bbbbbb HSL 0 0 73 javax.swing.plaf.ColorUIResource [UI] +Tree.selectionInsets 0,0,0,0 javax.swing.plaf.InsetsUIResource [UI] Tree.showCellFocusIndicator false Tree.textBackground #46494b HSL 204 3 28 javax.swing.plaf.ColorUIResource [UI] Tree.textForeground #bbbbbb HSL 0 0 73 javax.swing.plaf.ColorUIResource [UI] diff --git a/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt b/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt index 23f4fe7c8..d2203245b 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt @@ -1381,11 +1381,13 @@ Tree.repaintWholeRow true Tree.rightChildIndent 11 Tree.rowHeight 0 Tree.scrollsOnExpand true +Tree.selectionArc 0 Tree.selectionBackground #2675bf HSL 209 67 45 javax.swing.plaf.ColorUIResource [UI] Tree.selectionBorderColor #000000 HSL 0 0 0 javax.swing.plaf.ColorUIResource [UI] Tree.selectionForeground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI] Tree.selectionInactiveBackground #d3d3d3 HSL 0 0 83 javax.swing.plaf.ColorUIResource [UI] Tree.selectionInactiveForeground #000000 HSL 0 0 0 javax.swing.plaf.ColorUIResource [UI] +Tree.selectionInsets 0,0,0,0 javax.swing.plaf.InsetsUIResource [UI] Tree.showCellFocusIndicator false Tree.textBackground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI] Tree.textForeground #000000 HSL 0 0 0 javax.swing.plaf.ColorUIResource [UI] diff --git a/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt b/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt index 10bfa958a..0c7bb7c92 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt @@ -1401,11 +1401,13 @@ Tree.repaintWholeRow true Tree.rightChildIndent 11 Tree.rowHeight 0 Tree.scrollsOnExpand true +Tree.selectionArc 0 Tree.selectionBackground #00aa00 HSL 120 100 33 javax.swing.plaf.ColorUIResource [UI] Tree.selectionBorderColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI] Tree.selectionForeground #ffff00 HSL 60 100 50 javax.swing.plaf.ColorUIResource [UI] Tree.selectionInactiveBackground #888888 HSL 0 0 53 javax.swing.plaf.ColorUIResource [UI] Tree.selectionInactiveForeground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI] +Tree.selectionInsets 0,0,0,0 javax.swing.plaf.InsetsUIResource [UI] Tree.showCellFocusIndicator false Tree.textBackground #fff0ff HSL 300 100 97 javax.swing.plaf.ColorUIResource [UI] Tree.textForeground #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI] diff --git a/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt b/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt index 613bdb839..4baa1e5eb 100644 --- a/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt +++ b/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt @@ -1099,11 +1099,13 @@ Tree.repaintWholeRow Tree.rightChildIndent Tree.rowHeight Tree.scrollsOnExpand +Tree.selectionArc Tree.selectionBackground Tree.selectionBorderColor Tree.selectionForeground Tree.selectionInactiveBackground Tree.selectionInactiveForeground +Tree.selectionInsets Tree.showCellFocusIndicator Tree.textBackground Tree.textForeground From 3802c64be3fd47b66dc7d3bd8d7c0a78ec0b08dd Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Fri, 3 Jun 2022 09:33:19 +0200 Subject: [PATCH 2/2] Tree: better support for non-wide rounded selection --- .../flatlaf/ui/FlatMenuItemRenderer.java | 4 +- .../com/formdev/flatlaf/ui/FlatTreeUI.java | 54 +++++++++++++------ .../com/formdev/flatlaf/ui/FlatUIUtils.java | 27 ++-------- 3 files changed, 46 insertions(+), 39 deletions(-) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemRenderer.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemRenderer.java index 60f0e1b27..7d4707eca 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemRenderer.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemRenderer.java @@ -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 */ 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 20b6bc898..30f6ebd8c 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 @@ -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; @@ -377,7 +376,7 @@ public Map> 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 @@ -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 @@ -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 ); } } @@ -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) @@ -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; @@ -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)); } /** diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java index fa4abe0c3..200cb7fd5 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java @@ -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. *

* 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; @@ -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% @@ -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 )