UIDefaultsLoader: support derived option for mix(), tint() and shade() color functions; added unit tests for color functions

This commit is contained in:
Karl Tauber
2025-02-16 16:58:21 +01:00
parent 76f436726f
commit ebacad2d04
3 changed files with 290 additions and 9 deletions

View File

@@ -1109,13 +1109,14 @@ class UIDefaultsLoader
}
/**
* Syntax: mix(color1,color2[,weight]) or
* tint(color[,weight]) or
* shade(color[,weight])
* Syntax: mix(color1,color2[,weight][,options]) or
* tint(color[,weight][,options]) or
* shade(color[,weight][,options])
* - 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
* - options: [derived]
*/
private static Object parseColorMix( String color1Str, List<String> params, Function<String, String> resolver )
throws IllegalArgumentException
@@ -1124,18 +1125,31 @@ class UIDefaultsLoader
if( color1Str == null )
color1Str = params.get( i++ );
String color2Str = params.get( i++ );
int weight = (params.size() > i) ? parsePercentage( params.get( i ) ) : 50;
int weight = 50;
boolean derived = false;
if( params.size() > i ) {
String weightStr = params.get( i );
if( !weightStr.isEmpty() && Character.isDigit( weightStr.charAt( 0 ) ) ) {
weight = parsePercentage( weightStr );
i++;
}
}
if( params.size() > i ) {
String options = params.get( i );
derived = options.contains( "derived" );
}
// parse second color
ColorUIResource color2 = (ColorUIResource) parseColorOrFunction( resolver.apply( color2Str ), resolver );
if( color2 == null )
ColorUIResource color1 = (ColorUIResource) parseColorOrFunction( resolver.apply( color1Str ), resolver );
if( color1 == null )
return null;
// create function
ColorFunction function = new ColorFunctions.Mix( color2, weight );
ColorFunction function = new ColorFunctions.Mix2( color1, weight );
// parse first color, apply function and create mixed color
return parseFunctionBaseColor( color1Str, function, false, resolver );
return parseFunctionBaseColor( color2Str, function, derived, resolver );
}
/**

View File

@@ -224,6 +224,9 @@ public class ColorFunctions
if( functions.length == 1 && functions[0] instanceof Mix ) {
Mix mixFunction = (Mix) functions[0];
return mix( color, mixFunction.color2, mixFunction.weight / 100 );
} else if( functions.length == 1 && functions[0] instanceof Mix2 ) {
Mix2 mixFunction = (Mix2) functions[0];
return mix( mixFunction.color1, color, mixFunction.weight / 100 );
}
// convert RGB to HSL
@@ -386,7 +389,11 @@ public class ColorFunctions
//---- class Mix ----------------------------------------------------------
/**
* Mix two colors.
* Mix two colors using {@link ColorFunctions#mix(Color, Color, float)}.
* First color is passed to {@link #apply(float[])}.
* Second color is {@link #color2}.
* <p>
* Use {@link Mix2} to tint or shade color.
*
* @since 1.6
*/
@@ -420,4 +427,44 @@ public class ColorFunctions
return String.format( "mix(#%08x,%.0f%%)", color2.getRGB(), weight );
}
}
//---- class Mix2 ---------------------------------------------------------
/**
* Mix two colors using {@link ColorFunctions#mix(Color, Color, float)}.
* First color is {@link #color1}.
* Second color is passed to {@link #apply(float[])}.
*
* @since 3.6
*/
public static class Mix2
implements ColorFunction
{
public final Color color1;
public final float weight;
public Mix2( Color color1, float weight ) {
this.color1 = color1;
this.weight = weight;
}
@Override
public void apply( float[] hsla ) {
// convert from HSL to RGB because color mixing is done on RGB values
Color color2 = 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( "mix2(#%08x,%.0f%%)", color1.getRGB(), weight );
}
}
}