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 fd703cd2..bbfcbaf2 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java @@ -608,6 +608,7 @@ class UIDefaultsLoader case "changeSaturation":return parseColorChange( 1, params, resolver, reportError ); case "changeLightness": return parseColorChange( 2, params, resolver, reportError ); case "changeAlpha": return parseColorChange( 3, params, resolver, reportError ); + case "mix": return parseColorMix( params, resolver, reportError ); } } finally { parseColorDepth--; @@ -787,6 +788,34 @@ class UIDefaultsLoader return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError ); } + /** + * Syntax: mix(color1,color2[,weight]) + * - color1: a color (e.g. #f00) or a color function + * - color2: a color (e.g. #f00) or a color function + * - weight: the weight (in range 0-100%) to mix the two colors + * larger weight uses more of first color, smaller weight more of second color + */ + private static Object parseColorMix( List params, Function resolver, boolean reportError ) { + String color1Str = params.get( 0 ); + String color2Str = params.get( 1 ); + int weight = 50; + + if( params.size() > 2 ) + weight = parsePercentage( params.get( 2 ) ); + + // parse second color + String resolvedColor2Str = resolver.apply( color2Str ); + ColorUIResource color2 = (ColorUIResource) parseColorOrFunction( resolvedColor2Str, resolver, reportError ); + if( color2 == null ) + return null; + + // create function + ColorFunction function = new ColorFunctions.Mix( color2, weight ); + + // parse first color, apply function and create mixed color + return parseFunctionBaseColor( color1Str, function, false, resolver, reportError ); + } + private static Object parseFunctionBaseColor( String colorStr, ColorFunction function, boolean derived, Function resolver, boolean reportError ) { 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 index 65011cf4..0b7e99f9 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/ColorFunctions.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/ColorFunctions.java @@ -26,13 +26,16 @@ import java.awt.Color; public class ColorFunctions { public static Color applyFunctions( Color color, ColorFunction... functions ) { + // convert RGB to HSL float[] hsl = HSLColor.fromRGB( color ); float alpha = color.getAlpha() / 255f; float[] hsla = { hsl[0], hsl[1], hsl[2], alpha * 100 }; + // apply color functions for( ColorFunction function : functions ) function.apply( hsla ); + // convert HSL to RGB return HSLColor.toRGB( hsla[0], hsla[1], hsla[2], hsla[3] / 100 ); } @@ -208,4 +211,42 @@ public class ColorFunctions return String.format( "fade(%.0f%%)", amount ); } } + + //---- class Mix ---------------------------------------------------------- + + /** + * Mix two colors. + * + * @since 1.6 + */ + public static class Mix + implements ColorFunction + { + public final Color color2; + public final float weight; + + public Mix( Color color2, float weight ) { + this.color2 = color2; + this.weight = weight; + } + + @Override + public void apply( float[] hsla ) { + // convert from HSL to RGB because color mixing is done on RGB values + Color color1 = HSLColor.toRGB( hsla[0], hsla[1], hsla[2], hsla[3] / 100 ); + + // mix + Color color = mix( color1, color2, weight / 100 ); + + // convert RGB to HSL + float[] hsl = HSLColor.fromRGB( color ); + System.arraycopy( hsl, 0, hsla, 0, hsl.length ); + hsla[3] = (color.getAlpha() / 255f) * 100; + } + + @Override + public String toString() { + return String.format( "mix(#%08x,%.0f%%)", color2.getRGB(), weight ); + } + } } diff --git a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatCompletionProvider.java b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatCompletionProvider.java index 340d3494..25a4793d 100644 --- a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatCompletionProvider.java +++ b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatCompletionProvider.java @@ -370,6 +370,11 @@ class FlatCompletionProvider addFunction( "changeSaturation", hslChangeParams ); addFunction( "changeLightness", hslChangeParams ); addFunction( "changeAlpha", hslChangeParams ); + + addFunction( "mix", + "color1", colorParamDesc, + "color2", colorParamDesc, + "weight", "(optional) 0-100%, default is 50%" ); } private void addFunction( String name, String... paramNamesAndDescs ) { diff --git a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeTokenMaker.java b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeTokenMaker.java index 5a6a638e..895d2dac 100644 --- a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeTokenMaker.java +++ b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeTokenMaker.java @@ -68,6 +68,7 @@ public class FlatThemeTokenMaker tokenMap.put( "changeSaturation", TOKEN_FUNCTION ); tokenMap.put( "changeLightness", TOKEN_FUNCTION ); tokenMap.put( "changeAlpha", TOKEN_FUNCTION ); + tokenMap.put( "mix", TOKEN_FUNCTION ); tokenMap.put( "lazy", TOKEN_FUNCTION ); // function options diff --git a/flatlaf-theme-editor/theme-editor-test.properties b/flatlaf-theme-editor/theme-editor-test.properties index 2ebe8501..48ffbb82 100644 --- a/flatlaf-theme-editor/theme-editor-test.properties +++ b/flatlaf-theme-editor/theme-editor-test.properties @@ -54,3 +54,11 @@ Prop.colorFunc20 = changeHue(#f00,180) Prop.colorFunc21 = changeSaturation(#f00,50%) Prop.colorFunc22 = changeLightness(#f00,80%) Prop.colorFunc23 = changeAlpha(#f00,50%) + +Prop.colorFunc30 = mix(#f00,#0f0,10%) +Prop.colorFunc31 = mix(#f00,#0f0,25%) +Prop.colorFunc32 = mix(#f00,#0f0) +Prop.colorFunc33 = mix(#f00,#0f0,75%) +Prop.colorFunc34 = mix(#f00,#0f0,90%) +Prop.colorFunc35 = mix($Prop.color1,$Prop.color2) +Prop.colorFunc36 = mix($Prop.colorFunc20,$Prop.colorFunc30)