From 8ba5c9a39cd971963463bf0e9e497ac6d88f9da2 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Tue, 20 Aug 2019 11:13:03 +0200 Subject: [PATCH] Button: basic border and background --- .../java/com/formdev/flatlaf/FlatLaf.java | 16 +++ .../formdev/flatlaf/ui/FlatButtonBorder.java | 87 ++++++++++++++++ .../com/formdev/flatlaf/ui/FlatButtonUI.java | 65 ++++++++++++ .../com/formdev/flatlaf/ui/FlatUIUtils.java | 99 +++++++++++++++++++ .../formdev/flatlaf/FlatDarkLaf.properties | 9 ++ .../com/formdev/flatlaf/FlatLaf.properties | 6 ++ .../formdev/flatlaf/FlatLightLaf.properties | 9 ++ .../formdev/flatlaf/FlatComponentsTest.java | 53 ++++++++-- .../formdev/flatlaf/FlatComponentsTest.jfd | 44 ++++++++- .../formdev/flatlaf/FlatTestLaf.properties | 10 ++ 10 files changed, 390 insertions(+), 8 deletions(-) create mode 100644 flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonBorder.java create mode 100644 flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonUI.java create mode 100644 flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java index 4a2acbc2..b54eaa22 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Properties; import javax.swing.UIDefaults; +import javax.swing.UIDefaults.LazyValue; import javax.swing.plaf.ColorUIResource; import javax.swing.plaf.FontUIResource; import javax.swing.plaf.basic.BasicLookAndFeel; @@ -202,6 +203,10 @@ public abstract class FlatLaf case "true": return true; } + // borders + if( key.endsWith( ".border" ) ) + return parseBorder( value ); + // colors ColorUIResource color = parseColor( value ); if( color != null ) @@ -211,6 +216,17 @@ public abstract class FlatLaf return value; } + private Object parseBorder( String value ) { + return (LazyValue) t -> { + try { + return Class.forName( value ).newInstance(); + } catch( InstantiationException | IllegalAccessException | ClassNotFoundException ex ) { + ex.printStackTrace(); + return null; + } + }; + } + private ColorUIResource parseColor( String value ) { try { if( value.length() == 6 ) { diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonBorder.java new file mode 100644 index 00000000..c52a4635 --- /dev/null +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonBorder.java @@ -0,0 +1,87 @@ +/* + * Copyright 2019 FormDev Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.formdev.flatlaf.ui; + +import java.awt.Color; +import java.awt.Component; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.Paint; +import javax.swing.UIManager; +import javax.swing.plaf.basic.BasicBorders; + +/** + * Border for {@link javax.swing.JButton}. + * + * @author Karl Tauber + */ +public class FlatButtonBorder + extends BasicBorders.MarginBorder +{ + @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 ); + + float focusWidth = getFocusWidth(); + float lineWidth = getLineWidth(); + float arc = 6; //TODO + + g2.setPaint( getBorderColor( c ) ); + FlatUIUtils.drawRoundRectangle( g2, x, y, width, height, focusWidth, lineWidth, arc ); + } finally { + g2.dispose(); + } + } + + public Paint getBorderColor( Component c ) { + if( c.isEnabled() ) { + Color startColor = UIManager.getColor( "Button.startBorderColor" ); + Color endColor = UIManager.getColor( "Button.endBorderColor" ); + return (startColor.equals( endColor ) ) + ? startColor + : new GradientPaint( 0, getFocusWidth(), startColor, + 0, c.getHeight() - getFocusWidth() - 1f, endColor ); + } else + return UIManager.getColor( "Button.disabledBorderColor" ); + } + + @Override + public Insets getBorderInsets( Component c, Insets insets ) { + int w = Math.round( getFocusWidth() + getLineWidth() ); + + insets = super.getBorderInsets( c, insets ); + insets.top += w; + insets.left += w; + insets.bottom += w; + insets.right += w; + return insets; + } + + protected float getFocusWidth() { + //TODO + return 2; + } + + protected float getLineWidth() { + //TODO + return 1; + } +} diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonUI.java new file mode 100644 index 00000000..45471eb8 --- /dev/null +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonUI.java @@ -0,0 +1,65 @@ +/* + * Copyright 2019 FormDev Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.formdev.flatlaf.ui; + +import java.awt.Graphics; +import java.awt.Graphics2D; +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicButtonUI; + +/** + * Provides the Flat LaF UI delegate for {@link javax.swing.JButton}. + * + * @author Karl Tauber + */ +public class FlatButtonUI + extends BasicButtonUI +{ + private static ComponentUI instance; + + public static ComponentUI createUI( JComponent c ) { + if( instance == null ) + instance = new FlatButtonUI(); + return instance; + } + + @Override + public void update( Graphics g, JComponent c ) { + if( c.isOpaque() ) { + FlatUIUtils.paintParentBackground( g, c ); + + if( c.isEnabled() ) { + Graphics2D g2 = (Graphics2D) g.create(); + try { + FlatUIUtils.setRenderingHints( g2 ); + + //TODO + float focusWidth = 2; + float arc = 6; + + g2.setColor( c.getBackground() ); + FlatUIUtils.fillRoundRectangle( g2, 0, 0, c.getWidth(), c.getHeight(), focusWidth, arc ); + } finally { + g2.dispose(); + } + } + } + + paint( g, c ); + } +} 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 new file mode 100644 index 00000000..a886cd3f --- /dev/null +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java @@ -0,0 +1,99 @@ +/* + * Copyright 2019 FormDev Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.formdev.flatlaf.ui; + +import java.awt.Container; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.geom.Path2D; +import java.awt.geom.RoundRectangle2D; +import javax.swing.JComponent; + +/** + * Utility methods for UI delegates. + * + * @author Karl Tauber + */ +public class FlatUIUtils +{ + public static final boolean MAC_USE_QUARTZ = Boolean.getBoolean( "apple.awt.graphics.UseQuartz" ); + + /** + * Sets rendering hints used for painting. + */ + public static void setRenderingHints( Graphics2D g ) { + g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); + g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, + MAC_USE_QUARTZ ? RenderingHints.VALUE_STROKE_PURE : RenderingHints.VALUE_STROKE_NORMALIZE ); + } + + /** + * Draws a round rectangle. + */ + public static void drawRoundRectangle( Graphics2D g, int x, int y, int width, int height, + float focusWidth, float lineWidth, float arc ) + { + float arc2 = arc > lineWidth ? arc - lineWidth : 0f; + + RoundRectangle2D.Float r1 = new RoundRectangle2D.Float( + x + focusWidth, y + focusWidth, + width - focusWidth * 2, height - focusWidth * 2, arc, arc ); + RoundRectangle2D.Float r2 = new RoundRectangle2D.Float( + r1.x + lineWidth, r1.y + lineWidth, + r1.width - lineWidth * 2, r1.height - lineWidth * 2, arc2, arc2 ); + + Path2D border = new Path2D.Float( Path2D.WIND_EVEN_ODD ); + border.append( r1, false ); + border.append( r2, false ); + g.fill( border ); + } + + /** + * Fills a round rectangle. + */ + public static void fillRoundRectangle( Graphics2D g, int x, int y, int width, int height, + float focusWidth, float arc ) + { + g.fill( new RoundRectangle2D.Float( + x + focusWidth, y + focusWidth, + width - focusWidth * 2, height - focusWidth * 2, arc, arc ) ); + } + + /** + * Fill background with parent's background color because the visible component + * is smaller than its bounds (for the focus decoration). + */ + public static void paintParentBackground( Graphics g, JComponent c ) { + Container parent = findOpaqueParent( c ); + if( parent != null ) { + g.setColor( parent.getBackground() ); + g.fillRect( 0, 0, c.getWidth(), c.getHeight() ); + } + } + + /** + * Find the first parent that is opaque. + */ + private static Container findOpaqueParent( Container c ) { + while( (c = c.getParent()) != null ) { + if( c.isOpaque() ) + return c; + } + return null; + } +} diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatDarkLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatDarkLaf.properties index 2e69d1b3..cadeece0 100644 --- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatDarkLaf.properties +++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatDarkLaf.properties @@ -42,6 +42,15 @@ textText=@foreground window=@background +#---- Button ---- + +Button.background=4c5052 + +Button.startBorderColor=5e6060 +Button.endBorderColor=5e6060 +Button.disabledBorderColor=5e6060 + + #---- Label ---- Label.disabledForeground=808080 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 71533deb..1c43b0de 100644 --- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties +++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties @@ -16,4 +16,10 @@ #---- UI delegates ---- +ButtonUI=com.formdev.flatlaf.ui.FlatButtonUI LabelUI=com.formdev.flatlaf.ui.FlatLabelUI + + +#---- Button ---- + +Button.border=com.formdev.flatlaf.ui.FlatButtonBorder diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLightLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLightLaf.properties index 253db0d3..145dfcaa 100644 --- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLightLaf.properties +++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLightLaf.properties @@ -42,6 +42,15 @@ textText=@foreground window=@background +#---- Button ---- + +Button.background=ffffff + +Button.startBorderColor=bfbfbf +Button.endBorderColor=b3b3b3 +Button.disabledBorderColor=cfcfcf + + #---- Label ---- Label.disabledForeground=777777 diff --git a/flatlaf-core/src/test/java/com/formdev/flatlaf/FlatComponentsTest.java b/flatlaf-core/src/test/java/com/formdev/flatlaf/FlatComponentsTest.java index bae0f97a..f156cc0c 100644 --- a/flatlaf-core/src/test/java/com/formdev/flatlaf/FlatComponentsTest.java +++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/FlatComponentsTest.java @@ -50,32 +50,73 @@ public class FlatComponentsTest JLabel labelLabel = new JLabel(); JLabel label1 = new JLabel(); JLabel label2 = new JLabel(); + JLabel buttonLabel = new JLabel(); + JButton button1 = new JButton(); + JButton button2 = new JButton(); + JButton button3 = new JButton(); + JButton button4 = new JButton(); //======== this ======== setLayout(new GridBagLayout()); - ((GridBagLayout)getLayout()).columnWidths = new int[] {0, 0, 0, 0}; - ((GridBagLayout)getLayout()).rowHeights = new int[] {0, 0}; - ((GridBagLayout)getLayout()).columnWeights = new double[] {0.0, 0.0, 0.0, 1.0E-4}; - ((GridBagLayout)getLayout()).rowWeights = new double[] {0.0, 1.0E-4}; + ((GridBagLayout)getLayout()).columnWidths = new int[] {0, 0, 0, 0, 0, 0}; + ((GridBagLayout)getLayout()).rowHeights = new int[] {0, 0, 0}; + ((GridBagLayout)getLayout()).columnWeights = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 1.0E-4}; + ((GridBagLayout)getLayout()).rowWeights = new double[] {0.0, 0.0, 1.0E-4}; //---- labelLabel ---- labelLabel.setText("JLabel:"); add(labelLabel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, - new Insets(0, 0, 0, 5), 0, 0)); + new Insets(0, 0, 5, 5), 0, 0)); //---- label1 ---- label1.setText("enabled"); label1.setDisplayedMnemonic('E'); add(label1, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, - new Insets(0, 0, 0, 5), 0, 0)); + new Insets(0, 0, 5, 5), 0, 0)); //---- label2 ---- label2.setText("disabled"); label2.setDisplayedMnemonic('D'); label2.setEnabled(false); add(label2, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, + GridBagConstraints.CENTER, GridBagConstraints.BOTH, + new Insets(0, 0, 5, 5), 0, 0)); + + //---- buttonLabel ---- + buttonLabel.setText("JButton:"); + add(buttonLabel, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, + GridBagConstraints.CENTER, GridBagConstraints.BOTH, + new Insets(0, 0, 0, 5), 0, 0)); + + //---- button1 ---- + button1.setText("enabled"); + button1.setDisplayedMnemonicIndex(0); + add(button1, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, + GridBagConstraints.CENTER, GridBagConstraints.BOTH, + new Insets(0, 0, 0, 5), 0, 0)); + + //---- button2 ---- + button2.setText("disabled"); + button2.setDisplayedMnemonicIndex(0); + button2.setEnabled(false); + add(button2, new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0, + GridBagConstraints.CENTER, GridBagConstraints.BOTH, + new Insets(0, 0, 0, 5), 0, 0)); + + //---- button3 ---- + button3.setText("selected"); + button3.setSelected(true); + add(button3, new GridBagConstraints(3, 1, 1, 1, 0.0, 0.0, + GridBagConstraints.CENTER, GridBagConstraints.BOTH, + new Insets(0, 0, 0, 5), 0, 0)); + + //---- button4 ---- + button4.setText("selected disabled"); + button4.setSelected(true); + button4.setEnabled(false); + add(button4, new GridBagConstraints(4, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); // JFormDesigner - End of component initialization //GEN-END:initComponents diff --git a/flatlaf-core/src/test/java/com/formdev/flatlaf/FlatComponentsTest.jfd b/flatlaf-core/src/test/java/com/formdev/flatlaf/FlatComponentsTest.jfd index 8dae6c9a..fdf10791 100644 --- a/flatlaf-core/src/test/java/com/formdev/flatlaf/FlatComponentsTest.jfd +++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/FlatComponentsTest.jfd @@ -7,8 +7,8 @@ new FormModel { "JavaCodeGenerator.defaultVariableLocal": true } add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.GridBagLayout ) { - "$columnSpecs": "0, 0, 0" - "$rowSpecs": "0" + "$columnSpecs": "0, 0, 0, 0, 0" + "$rowSpecs": "0, 0" "$hGap": 5 "$vGap": 5 "$alignLeft": true @@ -34,6 +34,46 @@ new FormModel { }, new FormLayoutConstraints( class com.jformdesigner.runtime.GridBagConstraintsEx ) { "gridx": 2 } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "buttonLabel" + "text": "JButton:" + }, new FormLayoutConstraints( class com.jformdesigner.runtime.GridBagConstraintsEx ) { + "gridy": 1 + } ) + add( new FormComponent( "javax.swing.JButton" ) { + name: "button1" + "text": "enabled" + "displayedMnemonicIndex": 0 + }, new FormLayoutConstraints( class com.jformdesigner.runtime.GridBagConstraintsEx ) { + "gridx": 1 + "gridy": 1 + } ) + add( new FormComponent( "javax.swing.JButton" ) { + name: "button2" + "text": "disabled" + "displayedMnemonicIndex": 0 + "enabled": false + }, new FormLayoutConstraints( class com.jformdesigner.runtime.GridBagConstraintsEx ) { + "gridx": 2 + "gridy": 1 + } ) + add( new FormComponent( "javax.swing.JButton" ) { + name: "button3" + "text": "selected" + "selected": true + }, new FormLayoutConstraints( class com.jformdesigner.runtime.GridBagConstraintsEx ) { + "gridx": 3 + "gridy": 1 + } ) + add( new FormComponent( "javax.swing.JButton" ) { + name: "button4" + "text": "selected disabled" + "selected": true + "enabled": false + }, new FormLayoutConstraints( class com.jformdesigner.runtime.GridBagConstraintsEx ) { + "gridx": 4 + "gridy": 1 + } ) }, new FormLayoutConstraints( null ) { "location": new java.awt.Point( 0, 0 ) "size": new java.awt.Dimension( 580, 300 ) diff --git a/flatlaf-core/src/test/resources/com/formdev/flatlaf/FlatTestLaf.properties b/flatlaf-core/src/test/resources/com/formdev/flatlaf/FlatTestLaf.properties index 53c70137..35ab5c30 100644 --- a/flatlaf-core/src/test/resources/com/formdev/flatlaf/FlatTestLaf.properties +++ b/flatlaf-core/src/test/resources/com/formdev/flatlaf/FlatTestLaf.properties @@ -20,6 +20,16 @@ *.foreground=ff0000 +#---- Button ---- + +Button.background=ffffff + +Button.startBorderColor=ff0000 +Button.endBorderColor=0000ff +Button.disabledBorderColor=000088 + + #---- Label ---- +Label.foreground=008800 Label.disabledForeground=000088