diff --git a/CHANGELOG.md b/CHANGELOG.md index 4882c10a..da6f25ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,7 @@ FlatLaf Change Log - MenuItem: - Paint the selected icon when the item is selected. (PR #415) - Vertically align text if icons have different widths. (issue #437) +- Panel: Support painting background with rounded corners. (issue #367) - Added more color functions to class `ColorFunctions` for easy use in applications: `lighten()`, `darken()`, `saturate()`, `desaturate()`, `spin()`, `tint()`, `shade()` and `luma()`. diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java index 75000001..c9a3c480 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java @@ -582,17 +582,18 @@ class UIDefaultsLoader private static Object parseBorder( String value, Function resolver, List addonClassLoaders ) { if( value.indexOf( ',' ) >= 0 ) { - // top,left,bottom,right[,lineColor[,lineThickness]] + // top,left,bottom,right[,lineColor[,lineThickness[,arc]]] List parts = splitFunctionParams( value, ',' ); Insets insets = parseInsets( value ); ColorUIResource lineColor = (parts.size() >= 5) ? (ColorUIResource) parseColorOrFunction( resolver.apply( parts.get( 4 ) ), resolver, true ) : null; - float lineThickness = (parts.size() >= 6) ? parseFloat( parts.get( 5 ), true ) : 1f; + float lineThickness = (parts.size() >= 6 && !parts.get( 5 ).isEmpty()) ? parseFloat( parts.get( 5 ), true ) : 1f; + int arc = (parts.size() >= 7) ? parseInteger( parts.get( 6 ), true ) : 0; return (LazyValue) t -> { return (lineColor != null) - ? new FlatLineBorder( insets, lineColor, lineThickness ) + ? new FlatLineBorder( insets, lineColor, lineThickness, arc ) : new FlatEmptyBorder( insets ); }; } else diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatLineBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatLineBorder.java index 6f9000eb..fea886d8 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatLineBorder.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatLineBorder.java @@ -37,15 +37,18 @@ public class FlatLineBorder { private final Color lineColor; private final float lineThickness; + /** @since 2 */ private final int arc; public FlatLineBorder( Insets insets, Color lineColor ) { - this( insets, lineColor, 1f ); + this( insets, lineColor, 1f, 0 ); } - public FlatLineBorder( Insets insets, Color lineColor, float lineThickness ) { + /** @since 2 */ + public FlatLineBorder( Insets insets, Color lineColor, float lineThickness, int arc ) { super( insets ); this.lineColor = lineColor; this.lineThickness = lineThickness; + this.arc = arc; } public Color getLineColor() { @@ -56,13 +59,18 @@ public class FlatLineBorder return lineThickness; } + /** @since 2 */ + public int getArc() { + return arc; + } + @Override public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) { Graphics2D g2 = (Graphics2D) g.create(); try { FlatUIUtils.setRenderingHints( g2 ); FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height, - 0, 0, 0, scale( getLineThickness() ), 0, null, getLineColor(), null ); + 0, 0, 0, scale( getLineThickness() ), scale( getArc() ), null, getLineColor(), null ); } finally { g2.dispose(); } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPanelUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPanelUI.java index bec0549e..61ea6719 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPanelUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPanelUI.java @@ -16,14 +16,18 @@ package com.formdev.flatlaf.ui; +import java.awt.Graphics; +import java.awt.Graphics2D; import java.beans.PropertyChangeListener; import java.util.Map; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicPanelUI; +import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.util.LoggingFacade; +import com.formdev.flatlaf.util.UIScale; /** * Provides the Flat LaF UI delegate for {@link javax.swing.JPanel}. @@ -32,7 +36,7 @@ import com.formdev.flatlaf.util.LoggingFacade; * * @uiDefault Panel.font Font unused * @uiDefault Panel.background Color only used if opaque - * @uiDefault Panel.foreground Color + * @uiDefault Panel.foreground Color unused * @uiDefault Panel.border Border * * @author Karl Tauber @@ -41,6 +45,9 @@ public class FlatPanelUI extends BasicPanelUI implements StyleableUI { + // only used via styling (not in UI defaults) + /** @since 2 */ @Styleable protected int arc = -1; + private final boolean shared; private PropertyChangeListener propertyChangeListener; private Map oldStyleValues; @@ -113,4 +120,34 @@ public class FlatPanelUI public Map> getStyleableInfos( JComponent c ) { return FlatStylingSupport.getAnnotatedStyleableInfos( this ); } + + @Override + public void update( Graphics g, JComponent c ) { + // fill background + if( c.isOpaque() ) { + int width = c.getWidth(); + int height = c.getHeight(); + int arc = (this.arc >= 0) + ? this.arc + : ((c.getBorder() instanceof FlatLineBorder) + ? ((FlatLineBorder)c.getBorder()).getArc() + : 0); + + // fill background with parent color to avoid garbage in rounded corners + if( arc > 0 ) + FlatUIUtils.paintParentBackground( g, c ); + + g.setColor( c.getBackground() ); + if( arc > 0 ) { + // fill rounded rectangle if having rounded corners + Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g ); + FlatUIUtils.paintComponentBackground( (Graphics2D) g, 0, 0, width, height, + 0, UIScale.scale( arc ) ); + FlatUIUtils.resetRenderingHints( g, oldRenderingHints ); + } else + g.fillRect( 0, 0, width, height ); + } + + paint( g, c ); + } } diff --git a/flatlaf-core/src/test/java/com/formdev/flatlaf/TestUIDefaultsLoader.java b/flatlaf-core/src/test/java/com/formdev/flatlaf/TestUIDefaultsLoader.java index 34f6986d..65d50ae5 100644 --- a/flatlaf-core/src/test/java/com/formdev/flatlaf/TestUIDefaultsLoader.java +++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/TestUIDefaultsLoader.java @@ -22,8 +22,12 @@ import java.awt.Dimension; import java.awt.Font; import java.awt.Insets; import javax.swing.UIManager; +import javax.swing.border.Border; import javax.swing.UIDefaults.ActiveValue; +import javax.swing.UIDefaults.LazyValue; import org.junit.jupiter.api.Test; +import com.formdev.flatlaf.ui.FlatEmptyBorder; +import com.formdev.flatlaf.ui.FlatLineBorder; /** * @author Karl Tauber @@ -46,8 +50,8 @@ public class TestUIDefaultsLoader assertEquals( 1.23f, UIDefaultsLoader.parseValue( "dummy", "1.23", null ) ); assertEquals( 1.23f, UIDefaultsLoader.parseValue( "dummyWidth", "1.23", null ) ); - assertEquals( new Insets( 2,2,2,2 ), UIDefaultsLoader.parseValue( "dummyInsets", "2,2,2,2", null ) ); - assertEquals( new Dimension( 2,2 ), UIDefaultsLoader.parseValue( "dummySize", "2,2", null ) ); + assertEquals( new Insets( 1,2,3,4 ), UIDefaultsLoader.parseValue( "dummyInsets", "1,2,3,4", null ) ); + assertEquals( new Dimension( 1,2 ), UIDefaultsLoader.parseValue( "dummySize", "1,2", null ) ); assertEquals( new Color( 0xff0000 ), UIDefaultsLoader.parseValue( "dummy", "#f00", null ) ); assertEquals( new Color( 0xff0000 ), UIDefaultsLoader.parseValue( "dummyColor", "#f00", null ) ); } @@ -70,11 +74,35 @@ public class TestUIDefaultsLoader assertEquals( 1.23f, UIDefaultsLoader.parseValue( "dummy", "1.23", float.class ) ); assertEquals( 1.23f, UIDefaultsLoader.parseValue( "dummy", "1.23", Float.class ) ); - assertEquals( new Insets( 2,2,2,2 ), UIDefaultsLoader.parseValue( "dummy", "2,2,2,2", Insets.class ) ); - assertEquals( new Dimension( 2,2 ), UIDefaultsLoader.parseValue( "dummy", "2,2", Dimension.class ) ); + assertEquals( new Insets( 1,2,3,4 ), UIDefaultsLoader.parseValue( "dummy", "1,2,3,4", Insets.class ) ); + assertEquals( new Dimension( 1,2 ), UIDefaultsLoader.parseValue( "dummy", "1,2", Dimension.class ) ); assertEquals( new Color( 0xff0000 ), UIDefaultsLoader.parseValue( "dummy", "#f00", Color.class ) ); } + @Test + void parseBorders() { + Insets insets = new Insets( 1,2,3,4 ); + assertBorderEquals( new FlatEmptyBorder( insets ), "1,2,3,4" ); + assertBorderEquals( new FlatLineBorder( insets, Color.red ), "1,2,3,4,#f00" ); + assertBorderEquals( new FlatLineBorder( insets, Color.red, 2.5f, 0 ), "1,2,3,4,#f00,2.5" ); + assertBorderEquals( new FlatLineBorder( insets, Color.red, 2.5f, 6 ), "1,2,3,4,#f00,2.5,6" ); + assertBorderEquals( new FlatLineBorder( insets, Color.red, 1, 6 ), "1,2,3,4,#f00,,6" ); + } + + private void assertBorderEquals( Border expected, String actualStyle ) { + Border actual = (Border) ((LazyValue)UIDefaultsLoader.parseValue( "dummyBorder", actualStyle, null )).createValue( null ); + assertEquals( expected.getClass(), actual.getClass() ); + if( expected instanceof FlatEmptyBorder ) + assertEquals( ((FlatEmptyBorder)actual).getBorderInsets(), ((FlatEmptyBorder)expected).getBorderInsets() ); + if( expected instanceof FlatLineBorder ) { + FlatLineBorder a = (FlatLineBorder) actual; + FlatLineBorder e = (FlatLineBorder) expected; + assertEquals( a.getLineColor(), e.getLineColor() ); + assertEquals( a.getLineThickness(), e.getLineThickness() ); + assertEquals( a.getArc(), e.getArc() ); + } + } + @Test void parseFonts() { // style 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 12a1bd72..6475406e 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 @@ -386,6 +386,7 @@ public class TestFlatStyleableInfo FlatPanelUI ui = (FlatPanelUI) c.getUI(); Map> expected = expectedMap( + "arc", int.class ); assertMapEquals( expected, ui.getStyleableInfos( c ) ); 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 3d32ced3..c6dec738 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 @@ -529,6 +529,8 @@ public class TestFlatStyling JPanel c = new JPanel(); FlatPanelUI ui = (FlatPanelUI) c.getUI(); + ui.applyStyle( c, "arc: 8" ); + // JComponent properties ui.applyStyle( c, "background: #fff" ); ui.applyStyle( c, "foreground: #fff" ); diff --git a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatColorPipette.java b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatColorPipette.java index 107ed632..3eab0536 100644 --- a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatColorPipette.java +++ b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatColorPipette.java @@ -194,7 +194,7 @@ class FlatColorPipette zoom = UIScale.scale( 16 ); - getRootPane().setBorder( new FlatLineBorder( new Insets( 2, 2, 2, 2 ), Color.red, 2 ) ); + getRootPane().setBorder( new FlatLineBorder( new Insets( 2, 2, 2, 2 ), Color.red, 2, 0 ) ); view = new MagnifierView(); view.setPreferredSize( new Dimension( pixels * zoom, pixels * zoom ) );