diff --git a/CHANGELOG.md b/CHANGELOG.md index 54dbb1788..18549c0d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ FlatLaf Change Log `JPasswordField`, `JScrollPane` and `JTextField` are non-opaque if they have an outside focus border (e.g. IntelliJ and Darcula themes). (issues #20 and #17) +- Button: Hover and pressed background colors are now derived from actual button + background color. (issue #21) ## 0.16 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 853fa88b9..155b00a2c 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java @@ -36,6 +36,8 @@ import javax.swing.plaf.InsetsUIResource; import com.formdev.flatlaf.ui.FlatEmptyBorder; import com.formdev.flatlaf.ui.FlatLineBorder; +import com.formdev.flatlaf.util.ColorFunctions; +import com.formdev.flatlaf.util.DerivedColor; import com.formdev.flatlaf.util.ScaledNumber; /** @@ -101,7 +103,11 @@ static void loadDefaultsFromProperties( Class lookAndFeelClass, UIDefaults de continue; String value = resolveValue( properties, (String) e.getValue() ); - globals.put( key.substring( GLOBAL_PREFIX.length() ), parseValue( key, value, resolver ) ); + try { + globals.put( key.substring( GLOBAL_PREFIX.length() ), parseValue( key, value, resolver ) ); + } catch( RuntimeException ex ) { + logParseError( key, value, ex ); + } } // override UI defaults with globals @@ -122,13 +128,22 @@ static void loadDefaultsFromProperties( Class lookAndFeelClass, UIDefaults de continue; String value = resolveValue( properties, (String) e.getValue() ); - defaults.put( key, parseValue( key, value, resolver ) ); + try { + defaults.put( key, parseValue( key, value, resolver ) ); + } catch( RuntimeException ex ) { + logParseError( key, value, ex ); + } } } catch( IOException ex ) { ex.printStackTrace(); } } + private static void logParseError( String key, String value, RuntimeException ex ) { + System.err.println( "Failed to parse: '" + key + '=' + value + '\'' ); + System.err.println( " " + ex.getMessage() ); + } + private static String resolveValue( Properties properties, String value ) { if( !value.startsWith( VARIABLE_PREFIX ) ) return value; @@ -147,8 +162,7 @@ private static String resolveValue( Properties properties, String value ) { if( optional ) return "null"; - System.err.println( "variable or reference '" + value + "' not found" ); - throw new IllegalArgumentException( value ); + throw new IllegalArgumentException( "variable or reference '" + value + "' not found" ); } return resolveValue( properties, newValue ); @@ -264,8 +278,7 @@ private static Insets parseInsets( String value ) { Integer.parseInt( numbers.get( 2 ) ), Integer.parseInt( numbers.get( 3 ) ) ); } catch( NumberFormatException ex ) { - System.err.println( "invalid insets '" + value + "'" ); - throw ex; + throw new IllegalArgumentException( "invalid insets '" + value + "'" ); } } @@ -276,12 +289,14 @@ private static Dimension parseSize( String value ) { Integer.parseInt( numbers.get( 0 ) ), Integer.parseInt( numbers.get( 1 ) ) ); } catch( NumberFormatException ex ) { - System.err.println( "invalid size '" + value + "'" ); - throw ex; + throw new IllegalArgumentException( "invalid size '" + value + "'" ); } } private static ColorUIResource parseColor( String value, boolean reportError ) { + if( value.endsWith( ")" ) ) + return parseColorFunctions( value, reportError ); + try { int rgb = Integer.parseInt( value, 16 ); if( value.length() == 6 ) @@ -292,23 +307,78 @@ private static ColorUIResource parseColor( String value, boolean reportError ) { if( reportError ) throw new NumberFormatException( value ); } catch( NumberFormatException ex ) { - if( reportError ) { - System.err.println( "invalid color '" + value + "'" ); - throw ex; - } + if( reportError ) + throw new IllegalArgumentException( "invalid color '" + value + "'" ); + // not a color --> ignore } return null; } + private static ColorUIResource parseColorFunctions( String value, boolean reportError ) { + int paramsStart = value.indexOf( '(' ); + if( paramsStart < 0 ) { + if( reportError ) + throw new IllegalArgumentException( "missing opening parenthesis in function '" + value + "'" ); + return null; + } + + String function = value.substring( 0, paramsStart ).trim(); + List params = split( value.substring( paramsStart + 1, value.length() - 1 ), ',' ); + if( params.isEmpty() ) + throw new IllegalArgumentException( "missing parameters in function '" + value + "'" ); + + switch( function ) { + case "lighten": return parseColorLightenOrDarken( true, params, reportError ); + case "darken": return parseColorLightenOrDarken( false, params, reportError ); + } + + throw new IllegalArgumentException( "unknown color function '" + value + "'" ); + } + + /** + * Syntax: lighten(amount[,options]) or darken(amount[,options]) + * - amount: percentage 0-100% + * - options: [relative] [autoInverse] + */ + private static ColorUIResource parseColorLightenOrDarken( boolean lighten, List params, boolean reportError ) { + int amount = parsePercentage( params.get( 0 ) ); + boolean relative = false; + boolean autoInverse = false; + + if( params.size() >= 2 ) { + String options = params.get( 1 ); + relative = options.contains( "relative" ); + autoInverse = options.contains( "autoInverse" ); + } + + return new DerivedColor( lighten + ? new ColorFunctions.Lighten( amount, relative, autoInverse ) + : new ColorFunctions.Darken( amount, relative, autoInverse ) ); + } + + private static int parsePercentage( String value ) { + if( !value.endsWith( "%" ) ) + throw new NumberFormatException( "invalid percentage '" + value + "'" ); + + int val; + try { + val = Integer.parseInt( value.substring( 0, value.length() - 1 ) ); + } catch( NumberFormatException ex ) { + throw new NumberFormatException( "invalid percentage '" + value + "'" ); + } + + if( val < 0 || val > 100 ) + throw new IllegalArgumentException( "percentage out of range (0-100%) '" + value + "'" ); + return val; + } + private static Integer parseInteger( String value, boolean reportError ) { try { return Integer.parseInt( value ); } catch( NumberFormatException ex ) { - if( reportError ) { - System.err.println( "invalid integer '" + value + "'" ); - throw ex; - } + if( reportError ) + throw new NumberFormatException( "invalid integer '" + value + "'" ); } return null; } @@ -317,8 +387,7 @@ private static ScaledNumber parseScaledNumber( String value ) { try { return new ScaledNumber( Integer.parseInt( value ) ); } catch( NumberFormatException ex ) { - System.err.println( "invalid integer '" + value + "'" ); - throw ex; + throw new NumberFormatException( "invalid integer '" + value + "'" ); } } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatCheckBoxIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatCheckBoxIcon.java index e5ba8bb06..a5dc42369 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatCheckBoxIcon.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatCheckBoxIcon.java @@ -109,12 +109,13 @@ protected void paintIcon( Component c, Graphics2D g2 ) { paintBorder( g2 ); // paint background - g2.setColor( FlatButtonUI.buttonStateColor( c, + FlatUIUtils.setColor( g2, FlatButtonUI.buttonStateColor( c, selected ? selectedBackground : background, disabledBackground, focusedBackground, selected && selectedHoverBackground != null ? selectedHoverBackground : hoverBackground, - selected && selectedPressedBackground != null ? selectedPressedBackground : pressedBackground ) ); + selected && selectedPressedBackground != null ? selectedPressedBackground : pressedBackground ), + background ); paintBackground( g2 ); // paint checkmark diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatHelpButtonIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatHelpButtonIcon.java index 05590acbe..25ecc920b 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatHelpButtonIcon.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatHelpButtonIcon.java @@ -24,6 +24,7 @@ import java.awt.geom.Path2D; import javax.swing.UIManager; import com.formdev.flatlaf.ui.FlatButtonUI; +import com.formdev.flatlaf.ui.FlatUIUtils; /** * Help button icon for {@link javax.swing.JButton}. @@ -99,12 +100,12 @@ protected void paintIcon( Component c, Graphics2D g2 ) { g2.fill( new Ellipse2D.Float( focusWidth + 0.5f, focusWidth + 0.5f, 21, 21 ) ); // paint background - g2.setColor( FlatButtonUI.buttonStateColor( c, + FlatUIUtils.setColor( g2, FlatButtonUI.buttonStateColor( c, background, disabledBackground, focusedBackground, hoverBackground, - pressedBackground ) ); + pressedBackground ), background ); g2.fill( new Ellipse2D.Float( focusWidth + 1.5f, focusWidth + 1.5f, 19, 19 ) ); // paint question mark 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 index 3fb884d9c..19ee79273 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonUI.java @@ -190,7 +190,7 @@ public void update( Graphics g, JComponent c ) { float focusWidth = (border instanceof FlatBorder) ? scale( (float) this.focusWidth ) : 0; float arc = (border instanceof FlatButtonBorder || isToolBarButton( c )) ? scale( (float) this.arc ) : 0; - g2.setColor( background ); + FlatUIUtils.setColor( g2, background, isDefaultButton(c) ? defaultBackground : c.getBackground() ); FlatUIUtils.fillRoundRectangle( g2, 0, 0, c.getWidth(), c.getHeight(), focusWidth, arc ); } finally { g2.dispose(); 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 401d9a35a..034177ae3 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 @@ -38,6 +38,7 @@ import javax.swing.LookAndFeel; import javax.swing.UIManager; import javax.swing.plaf.ColorUIResource; +import com.formdev.flatlaf.util.DerivedColor; import com.formdev.flatlaf.util.JavaCompatibility; import com.formdev.flatlaf.util.UIScale; @@ -104,6 +105,12 @@ public static void setRenderingHints( Graphics2D g ) { MAC_USE_QUARTZ ? RenderingHints.VALUE_STROKE_PURE : RenderingHints.VALUE_STROKE_NORMALIZE ); } + public static void setColor( Graphics g, Color color, Color baseColor ) { + if( color instanceof DerivedColor ) + color = ((DerivedColor)color).derive( baseColor ); + g.setColor( color ); + } + /** * Draws a round rectangle. */ diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/ColorFunctions.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/ColorFunctions.java new file mode 100644 index 000000000..c246e9e58 --- /dev/null +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/ColorFunctions.java @@ -0,0 +1,102 @@ +/* + * 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.util; + +import java.awt.Color; + +/** + * Functions that modify colors. + * + * @author Karl Tauber + */ +public class ColorFunctions +{ + public static Color applyFunctions( Color color, ColorFunction[] functions ) { + float[] hsl = HSLColor.fromRGB( color ); + float alpha = color.getAlpha() / 255f; + + for( ColorFunction function : functions ) + function.apply( hsl ); + + return HSLColor.toRGB( hsl, alpha ); + } + + private static float clamp( float value ) { + return (value < 0) + ? 0 + : ((value > 100) + ? 100 + : value); + } + + //---- interface ColorFunction -------------------------------------------- + + public interface ColorFunction { + void apply( float[] hsl ); + } + + //---- class Lighten ------------------------------------------------------ + + /** + * Increase the lightness of a color in the HSL color space by an absolute + * or relative amount. + */ + public static class Lighten + implements ColorFunction + { + private final float amount; + private final boolean relative; + private final boolean autoInverse; + + public Lighten( float amount, boolean relative, boolean autoInverse ) { + this.amount = amount; + this.relative = relative; + this.autoInverse = autoInverse; + } + + @Override + public void apply( float[] hsl ) { + float amount2 = autoInverse && shouldInverse( hsl ) ? -amount : amount; + hsl[2] = clamp( relative + ? (hsl[2] * ((100 + amount2) / 100)) + : (hsl[2] + amount2) ); + } + + protected boolean shouldInverse( float[] hsl ) { + return hsl[2] >= 50; + } + } + + //---- class Darken ------------------------------------------------------- + + /** + * Decrease the lightness of a color in the HSL color space by an absolute + * or relative amount. + */ + public static class Darken + extends Lighten + { + public Darken( float amount, boolean relative, boolean autoInverse ) { + super( -amount, relative, autoInverse ); + } + + @Override + protected boolean shouldInverse( float[] hsl ) { + return hsl[2] < 50; + } + } +} diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/DerivedColor.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/DerivedColor.java new file mode 100644 index 000000000..e845b9c21 --- /dev/null +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/DerivedColor.java @@ -0,0 +1,43 @@ +/* + * 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.util; + +import java.awt.Color; +import javax.swing.plaf.ColorUIResource; +import com.formdev.flatlaf.util.ColorFunctions.ColorFunction; + +/** + * A (red) color that acts as a placeholder in UI defaults. + * The actual color is derived from another color, + * which is modified by the given color functions. + * + * @author Karl Tauber + */ +public class DerivedColor + extends ColorUIResource +{ + private final ColorFunction[] functions; + + public DerivedColor( ColorFunction... functions ) { + super( Color.red ); + this.functions = functions; + } + + public Color derive( Color baseColor ) { + return ColorFunctions.applyFunctions( baseColor, functions ); + } +} diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/HSLColor.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/HSLColor.java new file mode 100644 index 000000000..3197a4ec0 --- /dev/null +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/HSLColor.java @@ -0,0 +1,432 @@ +/* + * From http://tips4java.wordpress.com/2009/07/05/hsl-color/ + * + * License note on http://tips4java.wordpress.com/about/ + * "You are free to use and/or modify any or all code posted on the + * Java Tips Weblog without restriction. A credit in the code comments + * would be nice, but not in any way mandatory." + */ + +package com.formdev.flatlaf.util; + +import java.awt.Color; + +/** + * The HSLColor class provides methods to manipulate HSL (Hue, Saturation + * Luminance) values to create a corresponding Color object using the RGB + * ColorSpace. + * + * The HUE is the color, the Saturation is the purity of the color (with + * respect to grey) and Luminance is the brightness of the color (with respect + * to black and white) + * + * The Hue is specified as an angel between 0 - 360 degrees where red is 0, + * green is 120 and blue is 240. In between you have the colors of the rainbow. + * Saturation is specified as a percentage between 0 - 100 where 100 is fully + * saturated and 0 approaches gray. Luminance is specified as a percentage + * between 0 - 100 where 0 is black and 100 is white. + * + * In particular the HSL color space makes it easier change the Tone or Shade + * of a color by adjusting the luminance value. + */ +public class HSLColor +{ + private final Color rgb; + private final float[] hsl; + private final float alpha; + + /** + * Create a HSLColor object using an RGB Color object. + * + * @param rgb the RGB Color object + */ + public HSLColor(Color rgb) + { + this.rgb = rgb; + hsl = fromRGB( rgb ); + alpha = rgb.getAlpha() / 255.0f; + } + + /** + * Create a HSLColor object using individual HSL values and a default + * alpha value of 1.0. + * + * @param h is the Hue value in degrees between 0 - 360 + * @param s is the Saturation percentage between 0 - 100 + * @param l is the Lumanance percentage between 0 - 100 + */ + public HSLColor(float h, float s, float l) + { + this(h, s, l, 1.0f); + } + + /** + * Create a HSLColor object using individual HSL values. + * + * @param h the Hue value in degrees between 0 - 360 + * @param s the Saturation percentage between 0 - 100 + * @param l the Lumanance percentage between 0 - 100 + * @param alpha the alpha value between 0 - 1 + */ + public HSLColor(float h, float s, float l, float alpha) + { + hsl = new float[] {h, s, l}; + this.alpha = alpha; + rgb = toRGB(hsl, alpha); + } + + /** + * Create a HSLColor object using an an array containing the + * individual HSL values and with a default alpha value of 1. + * + * @param hsl array containing HSL values + */ + public HSLColor(float[] hsl) + { + this(hsl, 1.0f); + } + + /** + * Create a HSLColor object using an an array containing the + * individual HSL values. + * + * @param hsl array containing HSL values + * @param alpha the alpha value between 0 - 1 + */ + public HSLColor(float[] hsl, float alpha) + { + this.hsl = hsl; + this.alpha = alpha; + rgb = toRGB(hsl, alpha); + } + + /** + * Create a RGB Color object based on this HSLColor with a different + * Hue value. The degrees specified is an absolute value. + * + * @param degrees - the Hue value between 0 - 360 + * @return the RGB Color object + */ + public Color adjustHue(float degrees) + { + return toRGB(degrees, hsl[1], hsl[2], alpha); + } + + /** + * Create a RGB Color object based on this HSLColor with a different + * Luminance value. The percent specified is an absolute value. + * + * @param percent - the Luminance value between 0 - 100 + * @return the RGB Color object + */ + public Color adjustLuminance(float percent) + { + return toRGB(hsl[0], hsl[1], percent, alpha); + } + + /** + * Create a RGB Color object based on this HSLColor with a different + * Saturation value. The percent specified is an absolute value. + * + * @param percent - the Saturation value between 0 - 100 + * @return the RGB Color object + */ + public Color adjustSaturation(float percent) + { + return toRGB(hsl[0], percent, hsl[2], alpha); + } + + /** + * Create a RGB Color object based on this HSLColor with a different + * Shade. Changing the shade will return a darker color. The percent + * specified is a relative value. + * + * @param percent - the value between 0 - 100 + * @return the RGB Color object + */ + public Color adjustShade(float percent) + { + float multiplier = (100.0f - percent) / 100.0f; + float l = Math.max(0.0f, hsl[2] * multiplier); + + return toRGB(hsl[0], hsl[1], l, alpha); + } + + /** + * Create a RGB Color object based on this HSLColor with a different + * Tone. Changing the tone will return a lighter color. The percent + * specified is a relative value. + * + * @param percent - the value between 0 - 100 + * @return the RGB Color object + */ + public Color adjustTone(float percent) + { + float multiplier = (100.0f + percent) / 100.0f; + float l = Math.min(100.0f, hsl[2] * multiplier); + + return toRGB(hsl[0], hsl[1], l, alpha); + } + + /** + * Get the Alpha value. + * + * @return the Alpha value. + */ + public float getAlpha() + { + return alpha; + } + + /** + * Create a RGB Color object that is the complementary color of this + * HSLColor. This is a convenience method. The complementary color is + * determined by adding 180 degrees to the Hue value. + * @return the RGB Color object + */ + public Color getComplementary() + { + float hue = (hsl[0] + 180.0f) % 360.0f; + return toRGB(hue, hsl[1], hsl[2]); + } + + /** + * Get the Hue value. + * + * @return the Hue value. + */ + public float getHue() + { + return hsl[0]; + } + + /** + * Get the HSL values. + * + * @return the HSL values. + */ + public float[] getHSL() + { + return hsl; + } + + /** + * Get the Luminance value. + * + * @return the Luminance value. + */ + public float getLuminance() + { + return hsl[2]; + } + + /** + * Get the RGB Color object represented by this HDLColor. + * + * @return the RGB Color object. + */ + public Color getRGB() + { + return rgb; + } + + /** + * Get the Saturation value. + * + * @return the Saturation value. + */ + public float getSaturation() + { + return hsl[1]; + } + + @Override + public String toString() + { + String toString = + "HSLColor[h=" + hsl[0] + + ",s=" + hsl[1] + + ",l=" + hsl[2] + + ",alpha=" + alpha + "]"; + + return toString; + } + + /** + * Convert a RGB Color to it corresponding HSL values. + * + * @return an array containing the 3 HSL values. + */ + public static float[] fromRGB(Color color) + { + // Get RGB values in the range 0 - 1 + + float[] rgb = color.getRGBColorComponents( null ); + float r = rgb[0]; + float g = rgb[1]; + float b = rgb[2]; + + // Minimum and Maximum RGB values are used in the HSL calculations + + float min = Math.min(r, Math.min(g, b)); + float max = Math.max(r, Math.max(g, b)); + + // Calculate the Hue + + float h = 0; + + if (max == min) + h = 0; + else if (max == r) + h = ((60 * (g - b) / (max - min)) + 360) % 360; + else if (max == g) + h = (60 * (b - r) / (max - min)) + 120; + else if (max == b) + h = (60 * (r - g) / (max - min)) + 240; + + // Calculate the Luminance + + float l = (max + min) / 2; +// System.out.println(max + " : " + min + " : " + l); + + // Calculate the Saturation + + float s = 0; + + if (max == min) + s = 0; + else if (l <= .5f) + s = (max - min) / (max + min); + else + s = (max - min) / (2 - max - min); + +// System.out.println(new HSLColor( new float[] {h, s * 100, l * 100} )); + return new float[] {h, s * 100, l * 100}; + } + + /** + * Convert HSL values to a RGB Color with a default alpha value of 1. + * H (Hue) is specified as degrees in the range 0 - 360. + * S (Saturation) is specified as a percentage in the range 1 - 100. + * L (Lumanance) is specified as a percentage in the range 1 - 100. + * + * @param hsl an array containing the 3 HSL values + * + * @returns the RGB Color object + */ + public static Color toRGB(float[] hsl) + { + return toRGB(hsl, 1.0f); + } + + /** + * Convert HSL values to a RGB Color. + * H (Hue) is specified as degrees in the range 0 - 360. + * S (Saturation) is specified as a percentage in the range 1 - 100. + * L (Lumanance) is specified as a percentage in the range 1 - 100. + * + * @param hsl an array containing the 3 HSL values + * @param alpha the alpha value between 0 - 1 + * + * @returns the RGB Color object + */ + public static Color toRGB(float[] hsl, float alpha) + { + return toRGB(hsl[0], hsl[1], hsl[2], alpha); + } + + /** + * Convert HSL values to a RGB Color with a default alpha value of 1. + * + * @param h Hue is specified as degrees in the range 0 - 360. + * @param s Saturation is specified as a percentage in the range 1 - 100. + * @param l Lumanance is specified as a percentage in the range 1 - 100. + * + * @returns the RGB Color object + */ + public static Color toRGB(float h, float s, float l) + { + return toRGB(h, s, l, 1.0f); + } + + /** + * Convert HSL values to a RGB Color. + * + * @param h Hue is specified as degrees in the range 0 - 360. + * @param s Saturation is specified as a percentage in the range 1 - 100. + * @param l Lumanance is specified as a percentage in the range 1 - 100. + * @param alpha the alpha value between 0 - 1 + * + * @returns the RGB Color object + */ + public static Color toRGB(float h, float s, float l, float alpha) + { + if (s <0.0f || s > 100.0f) + { + String message = "Color parameter outside of expected range - Saturation"; + throw new IllegalArgumentException( message ); + } + + if (l <0.0f || l > 100.0f) + { + String message = "Color parameter outside of expected range - Luminance"; + throw new IllegalArgumentException( message ); + } + + if (alpha <0.0f || alpha > 1.0f) + { + String message = "Color parameter outside of expected range - Alpha"; + throw new IllegalArgumentException( message ); + } + + // Formula needs all values between 0 - 1. + + h = h % 360.0f; + h /= 360f; + s /= 100f; + l /= 100f; + + float q = 0; + + if (l < 0.5) + q = l * (1 + s); + else + q = (l + s) - (s * l); + + float p = 2 * l - q; + + float r = Math.max(0, HueToRGB(p, q, h + (1.0f / 3.0f))); + float g = Math.max(0, HueToRGB(p, q, h)); + float b = Math.max(0, HueToRGB(p, q, h - (1.0f / 3.0f))); + + r = Math.min(r, 1.0f); + g = Math.min(g, 1.0f); + b = Math.min(b, 1.0f); + + return new Color(r, g, b, alpha); + } + + private static float HueToRGB(float p, float q, float h) + { + if (h < 0) h += 1; + + if (h > 1 ) h -= 1; + + if (6 * h < 1) + { + return p + ((q - p) * 6 * h); + } + + if (2 * h < 1 ) + { + return q; + } + + if (3 * h < 2) + { + return p + ( (q - p) * 6 * ((2.0f / 3.0f) - h) ); + } + + return p; + } +} 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 4b31f608c..437ccaac8 100644 --- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatDarkLaf.properties +++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatDarkLaf.properties @@ -31,6 +31,10 @@ @cellFocusColor=000000 @icon=adadad +# Button +@buttonHoverBackground=lighten(3%,autoInverse) +@buttonPressedBackground=lighten(6%,autoInverse) + #---- globals ---- @@ -63,8 +67,8 @@ window=@background #---- Button ---- Button.background=4c5052 -Button.hoverBackground=525658 -Button.pressedBackground=5c6164 +Button.hoverBackground=@buttonHoverBackground +Button.pressedBackground=@buttonPressedBackground Button.borderColor=5e6060 Button.disabledBorderColor=5e6060 @@ -73,8 +77,8 @@ Button.hoverBorderColor=@@Button.focusedBorderColor Button.default.background=365880 Button.default.foreground=bbbbbb -Button.default.hoverBackground=3d6185 -Button.default.pressedBackground=43688c +Button.default.hoverBackground=@buttonHoverBackground +Button.default.pressedBackground=@buttonPressedBackground Button.default.borderColor=4c708c Button.default.hoverBorderColor=537699 Button.default.focusedBorderColor=537699 @@ -95,8 +99,8 @@ CheckBox.icon.hoverBorderColor=@@CheckBox.icon.focusedBorderColor CheckBox.icon.selectedFocusedBorderColor=466D94 CheckBox.icon.background=43494A CheckBox.icon.disabledBackground=@background -CheckBox.icon.hoverBackground=4c5052 -CheckBox.icon.pressedBackground=@@Button.pressedBackground +CheckBox.icon.hoverBackground=@buttonHoverBackground +CheckBox.icon.pressedBackground=@buttonPressedBackground CheckBox.icon.selectedBackground=43494A CheckBox.icon.checkmarkColor=A7A7A7 CheckBox.icon.disabledCheckmarkColor=606060 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 c0861b9df..e309ed436 100644 --- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLightLaf.properties +++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLightLaf.properties @@ -31,6 +31,10 @@ @cellFocusColor=000000 @icon=afafaf +# Button +@buttonHoverBackground=darken(3%,autoInverse) +@buttonPressedBackground=darken(10%,autoInverse) + #---- globals ---- @@ -64,8 +68,8 @@ window=@background Button.background=ffffff Button.focusedBackground=e3f1fa -Button.hoverBackground=f8f8f8 -Button.pressedBackground=dfdfdf +Button.hoverBackground=@buttonHoverBackground +Button.pressedBackground=@buttonPressedBackground Button.borderColor=bfbfbf Button.disabledBorderColor=cfcfcf @@ -75,8 +79,8 @@ Button.hoverBorderColor=@@Button.focusedBorderColor Button.default.background=@@Button.background Button.default.foreground=@foreground Button.default.focusedBackground=@@Button.focusedBackground -Button.default.hoverBackground=@@Button.hoverBackground -Button.default.pressedBackground=@@Button.pressedBackground +Button.default.hoverBackground=@buttonHoverBackground +Button.default.pressedBackground=@buttonPressedBackground Button.default.borderColor=4D89C9 Button.default.hoverBorderColor=@@Button.hoverBorderColor Button.default.focusedBorderColor=@@Button.focusedBorderColor @@ -97,8 +101,8 @@ CheckBox.icon.hoverBorderColor=@@CheckBox.icon.focusedBorderColor CheckBox.icon.background=FFFFFF CheckBox.icon.disabledBackground=@background CheckBox.icon.focusedBackground=@@Button.focusedBackground -CheckBox.icon.hoverBackground=@@Button.hoverBackground -CheckBox.icon.pressedBackground=@@Button.pressedBackground +CheckBox.icon.hoverBackground=@buttonHoverBackground +CheckBox.icon.pressedBackground=@buttonPressedBackground CheckBox.icon.selectedBackground=FFFFFF CheckBox.icon.checkmarkColor=4D89C9 CheckBox.icon.disabledCheckmarkColor=ABABAB