From 029f273dd993d791abb4538fc4662ea418b9856d Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Tue, 21 May 2024 13:37:11 +0200 Subject: [PATCH] Label: support painting background with rounded corners (issue #842) Demo: added rounded panels and labels to "More Components" tab --- CHANGELOG.md | 4 ++ .../com/formdev/flatlaf/UIDefaultsLoader.java | 12 ++-- .../com/formdev/flatlaf/ui/FlatBorder.java | 2 +- .../com/formdev/flatlaf/ui/FlatLabelUI.java | 9 +++ .../formdev/flatlaf/ui/FlatLineBorder.java | 28 +++++++- .../com/formdev/flatlaf/ui/FlatPanelUI.java | 18 +++-- .../formdev/flatlaf/TestUIDefaultsLoader.java | 2 + .../flatlaf/ui/TestFlatStyleableInfo.java | 3 +- .../flatlaf/ui/TestFlatStyleableValue.java | 1 + .../formdev/flatlaf/ui/TestFlatStyling.java | 1 + .../flatlaf/demo/MoreComponentsPanel.java | 66 +++++++++++++++++-- .../flatlaf/demo/MoreComponentsPanel.jfd | 62 ++++++++++++++++- 12 files changed, 185 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85e67206..083b95a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ FlatLaf Change Log ## 3.5-SNAPSHOT +#### New features and improvements + +- Label: Support painting background with rounded corners. (issue #842) + #### Incompatibilities - ProgressBar: Log warning (including stack trace) when uninstalling 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 af21c3d0..c9434274 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java @@ -638,14 +638,18 @@ class UIDefaultsLoader // Syntax: top,left,bottom,right[,lineColor[,lineThickness[,arc]]] List parts = splitFunctionParams( value, ',' ); Insets insets = parseInsets( value ); - ColorUIResource lineColor = (parts.size() >= 5) + ColorUIResource lineColor = (parts.size() >= 5 && !parts.get( 4 ).isEmpty()) ? (ColorUIResource) parseColorOrFunction( resolver.apply( parts.get( 4 ) ), resolver ) : null; - float lineThickness = (parts.size() >= 6 && !parts.get( 5 ).isEmpty()) ? parseFloat( parts.get( 5 ) ) : 1f; - int arc = (parts.size() >= 7) ? parseInteger( parts.get( 6 ) ) : 0; + float lineThickness = (parts.size() >= 6 && !parts.get( 5 ).isEmpty()) + ? parseFloat( parts.get( 5 ) ) + : 1f; + int arc = (parts.size() >= 7) && !parts.get( 6 ).isEmpty() + ? parseInteger( parts.get( 6 ) ) + : 0; return (LazyValue) t -> { - return (lineColor != null) + return (lineColor != null || arc > 0) ? new FlatLineBorder( insets, lineColor, lineThickness, arc ) : new FlatEmptyBorder( insets ); }; diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatBorder.java index 2903998d..7af299d7 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatBorder.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatBorder.java @@ -277,7 +277,7 @@ public class FlatBorder } /** - * Returns the (unscaled) arc diameter of the border. + * Returns the (unscaled) arc diameter of the border corners. */ protected int getArc( Component c ) { return 0; diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatLabelUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatLabelUI.java index 249a701b..afcf8923 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatLabelUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatLabelUI.java @@ -64,6 +64,9 @@ public class FlatLabelUI { @Styleable protected Color disabledForeground; + // only used via styling (not in UI defaults) + /** @since 3.5 */ @Styleable protected int arc = -1; + private final boolean shared; private boolean defaults_initialized = false; private Map oldStyleValues; @@ -244,6 +247,12 @@ public class FlatLabelUI return false; } + @Override + public void update( Graphics g, JComponent c ) { + FlatPanelUI.fillRoundedBackground( g, c, arc ); + paint( g, c ); + } + static Graphics createGraphicsHTMLTextYCorrection( Graphics g, JComponent c ) { return (c.getClientProperty( BasicHTML.propertyKey ) != null) ? HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) 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 888f2cb2..cec6fd42 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 @@ -26,10 +26,14 @@ import javax.swing.JComponent; /** * Line border for various components. - * + *

* Paints a scaled (usually 1px thick) line around the component. * The line thickness is not added to the border insets. * The insets should be at least have line thickness (usually 1,1,1,1). + *

+ * For {@link javax.swing.JPanel} and {@link javax.swing.JLabel}, this border + * can be used paint rounded background (if line color is {@code null}) or + * paint rounded line border with rounded background. * * @author Karl Tauber */ @@ -52,15 +56,28 @@ public class FlatLineBorder this.arc = arc; } + /** @since 3.5 */ + public FlatLineBorder( Insets insets, int arc ) { + this( insets, null, 0, arc ); + } + public Color getLineColor() { return lineColor; } + /** + * Returns the (unscaled) line thickness used to paint the border. + * The line thickness does not affect the border insets. + */ public float getLineThickness() { return lineThickness; } - /** @since 2 */ + /** + * Returns the (unscaled) arc diameter of the border corners. + * + * @since 2 + */ public int getArc() { return arc; } @@ -70,11 +87,16 @@ public class FlatLineBorder if( c instanceof JComponent && ((JComponent)c).getClientProperty( FlatPopupFactory.KEY_POPUP_USES_NATIVE_BORDER ) != null ) return; + Color lineColor = getLineColor(); + float lineThickness = getLineThickness(); + if( lineColor == null || lineThickness <= 0 ) + return; + Graphics2D g2 = (Graphics2D) g.create(); try { FlatUIUtils.setRenderingHints( g2 ); FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height, - 0, 0, 0, scale( getLineThickness() ), scale( getArc() ), null, getLineColor(), null ); + 0, 0, 0, scale( lineThickness ), scale( getArc() ), null, lineColor, 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 d51bb2f8..5b3cc55d 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 @@ -25,6 +25,7 @@ import java.util.Map; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.LookAndFeel; +import javax.swing.border.Border; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicPanelUI; import com.formdev.flatlaf.FlatClientProperties; @@ -160,11 +161,18 @@ public class FlatPanelUI @Override public void update( Graphics g, JComponent c ) { - int arc = (this.arc >= 0) - ? this.arc - : ((c.getBorder() instanceof FlatLineBorder) - ? ((FlatLineBorder)c.getBorder()).getArc() + fillRoundedBackground( g, c, arc ); + paint( g, c ); + } + + /** @since 3.5 */ + public static void fillRoundedBackground( Graphics g, JComponent c, int arc ) { + if( arc < 0 ) { + Border border = c.getBorder(); + arc = ((border instanceof FlatLineBorder) + ? ((FlatLineBorder)border).getArc() : 0); + } // fill background if( c.isOpaque() ) { @@ -185,8 +193,6 @@ public class FlatPanelUI 0, UIScale.scale( arc ) ); FlatUIUtils.resetRenderingHints( g, oldRenderingHints ); } - - paint( g, c ); } @Override 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 942bf212..a303f31b 100644 --- a/flatlaf-core/src/test/java/com/formdev/flatlaf/TestUIDefaultsLoader.java +++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/TestUIDefaultsLoader.java @@ -84,10 +84,12 @@ public class TestUIDefaultsLoader void parseBorders() { Insets insets = new Insets( 1,2,3,4 ); assertBorderEquals( new FlatEmptyBorder( 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" ); + assertBorderEquals( new FlatLineBorder( insets, null, 1, 6 ), "1,2,3,4,,,6" ); } private void assertBorderEquals( Border expected, String actualStyle ) { 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 3960b2d5..c18456d7 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 @@ -253,7 +253,8 @@ public class TestFlatStyleableInfo FlatLabelUI ui = (FlatLabelUI) c.getUI(); Map> expected = expectedMap( - "disabledForeground", Color.class + "disabledForeground", Color.class, + "arc", int.class ); assertMapEquals( expected, ui.getStyleableInfos( c ) ); diff --git a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableValue.java b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableValue.java index 47d32e63..18d1f315 100644 --- a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableValue.java +++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableValue.java @@ -355,6 +355,7 @@ public class TestFlatStyleableValue FlatLabelUI ui = (FlatLabelUI) c.getUI(); testColor( c, ui, "disabledForeground", 0x123456 ); + testInteger( c, ui, "arc", 123 ); } @Test 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 035e631a..010dfc6c 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 @@ -412,6 +412,7 @@ public class TestFlatStyling FlatLabelUI ui = (FlatLabelUI) c.getUI(); ui.applyStyle( c, "disabledForeground: #fff" ); + ui.applyStyle( c, "arc: 8" ); // JComponent properties ui.applyStyle( c, "background: #fff" ); diff --git a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/MoreComponentsPanel.java b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/MoreComponentsPanel.java index 1613fb3d..5a19d4c7 100644 --- a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/MoreComponentsPanel.java +++ b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/MoreComponentsPanel.java @@ -118,6 +118,14 @@ class MoreComponentsPanel JLabel label5 = new JLabel(); JPanel panel13 = new JPanel(); JLabel label6 = new JLabel(); + JLabel panelLabel = new JLabel(); + JPanel panel5 = new JPanel(); + JLabel label9 = new JLabel(); + JPanel panel4 = new JPanel(); + JLabel label8 = new JLabel(); + JLabel labelLabel = new JLabel(); + JLabel label13 = new JLabel(); + JLabel label10 = new JLabel(); //======== this ======== setLayout(new MigLayout( @@ -140,7 +148,9 @@ class MoreComponentsPanel "[]" + "[]" + "[]" + - "[100,top]")); + "[100,top]" + + "[50,top]" + + "[]")); //---- scrollPaneLabel ---- scrollPaneLabel.setText("JScrollPane:"); @@ -441,7 +451,7 @@ class MoreComponentsPanel //======== panel10 ======== { - panel10.setBackground(new Color(217, 163, 67)); + panel10.setBackground(new Color(0xd9a343)); panel10.setLayout(new BorderLayout()); //---- label1 ---- @@ -454,7 +464,7 @@ class MoreComponentsPanel //======== panel11 ======== { - panel11.setBackground(new Color(98, 181, 67)); + panel11.setBackground(new Color(0x62b543)); panel11.setLayout(new BorderLayout()); //---- label2 ---- @@ -474,7 +484,7 @@ class MoreComponentsPanel //======== panel12 ======== { - panel12.setBackground(new Color(242, 101, 34)); + panel12.setBackground(new Color(0xf26522)); panel12.setLayout(new BorderLayout()); //---- label5 ---- @@ -487,7 +497,7 @@ class MoreComponentsPanel //======== panel13 ======== { - panel13.setBackground(new Color(64, 182, 224)); + panel13.setBackground(new Color(0x40b6e0)); panel13.setLayout(new BorderLayout()); //---- label6 ---- @@ -502,6 +512,52 @@ class MoreComponentsPanel } add(splitPane3, "cell 1 11 4 1,grow"); + //---- panelLabel ---- + panelLabel.setText("JPanel:"); + add(panelLabel, "cell 0 12"); + + //======== panel5 ======== + { + panel5.putClientProperty("FlatLaf.style", "arc: 16; background: darken($Panel.background,5%)"); + panel5.setLayout(new BorderLayout()); + + //---- label9 ---- + label9.setText("rounded background"); + label9.setHorizontalAlignment(SwingConstants.CENTER); + panel5.add(label9, BorderLayout.CENTER); + } + add(panel5, "cell 1 12 4 1,growy,width 150"); + + //======== panel4 ======== + { + panel4.putClientProperty("FlatLaf.style", "border: 1,1,1,1,@disabledForeground,1,16; background: darken($Panel.background,5%)"); + panel4.setLayout(new BorderLayout()); + + //---- label8 ---- + label8.setText("rounded border"); + label8.setHorizontalAlignment(SwingConstants.CENTER); + panel4.add(label8, BorderLayout.CENTER); + } + add(panel4, "cell 1 12 4 1,growy,width 150"); + + //---- labelLabel ---- + labelLabel.setText("JLabel:"); + add(labelLabel, "cell 0 13"); + + //---- label13 ---- + label13.setText("rounded background"); + label13.putClientProperty("FlatLaf.style", "arc: 999; border: 2,10,2,10"); + label13.setBackground(new Color(0xb8e4f3)); + label13.setForeground(new Color(0x135b76)); + add(label13, "cell 1 13 4 1"); + + //---- label10 ---- + label10.setText("rounded border"); + label10.putClientProperty("FlatLaf.style", "border: 2,10,2,10,#135b76,1,999"); + label10.setBackground(new Color(0xb8e4f3)); + label10.setForeground(new Color(0x135b76)); + add(label10, "cell 1 13 4 1"); + //---- buttonGroup1 ---- ButtonGroup buttonGroup1 = new ButtonGroup(); buttonGroup1.add(toggleButton1); diff --git a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/MoreComponentsPanel.jfd b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/MoreComponentsPanel.jfd index b5baf3a4..9c7a2945 100644 --- a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/MoreComponentsPanel.jfd +++ b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/MoreComponentsPanel.jfd @@ -1,4 +1,4 @@ -JFDML JFormDesigner: "7.0.5.0.404" Java: "17.0.2" encoding: "UTF-8" +JFDML JFormDesigner: "8.2.2.0.9999" Java: "21.0.1" encoding: "UTF-8" new FormModel { contentType: "form/swing" @@ -9,7 +9,7 @@ new FormModel { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { "$layoutConstraints": "insets dialog,hidemode 3" "$columnConstraints": "[][][][][]" - "$rowConstraints": "[][][][][][][][][][][][100,top]" + "$rowConstraints": "[][][][][][][][][][][][100,top][50,top][]" } ) { name: "this" add( new FormComponent( "javax.swing.JLabel" ) { @@ -467,6 +467,62 @@ new FormModel { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 1 11 4 1,grow" } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "panelLabel" + "text": "JPanel:" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 12" + } ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) { + name: "panel5" + "$client.FlatLaf.style": "arc: 16; background: darken($Panel.background,5%)" + add( new FormComponent( "javax.swing.JLabel" ) { + name: "label9" + "text": "rounded background" + "horizontalAlignment": 0 + }, new FormLayoutConstraints( class java.lang.String ) { + "value": "Center" + } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 12 4 1,growy,width 150" + } ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) { + name: "panel4" + "$client.FlatLaf.style": "border: 1,1,1,1,@disabledForeground,1,16; background: darken($Panel.background,5%)" + add( new FormComponent( "javax.swing.JLabel" ) { + name: "label8" + "text": "rounded border" + "horizontalAlignment": 0 + }, new FormLayoutConstraints( class java.lang.String ) { + "value": "Center" + } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 12 4 1,growy,width 150" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "labelLabel" + "text": "JLabel:" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 13" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "label13" + "text": "rounded background" + "$client.FlatLaf.style": "arc: 999; border: 2,10,2,10" + "background": new java.awt.Color( 184, 228, 243, 255 ) + "foreground": new java.awt.Color( 19, 91, 118, 255 ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 13 4 1" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "label10" + "text": "rounded border" + "$client.FlatLaf.style": "border: 2,10,2,10,#135b76,1,999" + "background": new java.awt.Color( 184, 228, 243, 255 ) + "foreground": new java.awt.Color( 19, 91, 118, 255 ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 13 4 1" + } ) }, new FormLayoutConstraints( null ) { "location": new java.awt.Point( 0, 0 ) "size": new java.awt.Dimension( 700, 550 ) @@ -474,7 +530,7 @@ new FormModel { add( new FormNonVisual( "javax.swing.ButtonGroup" ) { name: "buttonGroup1" }, new FormLayoutConstraints( null ) { - "location": new java.awt.Point( 0, 560 ) + "location": new java.awt.Point( 0, 600 ) } ) } }