UIDefaultsLoader: support lazy option for spin(), changeHue(), changeSaturation(), changeLightness(), changeAlpha(), mix(), tint() and shade() color functions; added unit tests for lazy color functions

This commit is contained in:
Karl Tauber
2025-02-25 10:42:44 +01:00
parent 022a67929a
commit a4dc1b4151
2 changed files with 115 additions and 24 deletions

View File

@@ -830,6 +830,7 @@ class UIDefaultsLoader
try { try {
switch( function ) { switch( function ) {
case "if": return parseColorIf( value, params, resolver ); case "if": return parseColorIf( value, params, resolver );
case "lazy": return parseColorLazy( value, params, resolver );
case "systemColor": return parseColorSystemColor( value, params, resolver ); case "systemColor": return parseColorSystemColor( value, params, resolver );
case "rgb": return parseColorRgbOrRgba( false, params, resolver ); case "rgb": return parseColorRgbOrRgba( false, params, resolver );
case "rgba": return parseColorRgbOrRgba( true, params, resolver ); case "rgba": return parseColorRgbOrRgba( true, params, resolver );
@@ -877,6 +878,32 @@ class UIDefaultsLoader
return parseColorOrFunction( resolver.apply( ifValue ), resolver ); return parseColorOrFunction( resolver.apply( ifValue ), resolver );
} }
/**
* Syntax: lazy(uiKey)
* <p>
* This "lazy" function is only used if the "lazy" is passed as parameter to another
* color function. Otherwise, the general "lazy" function is used.
* <p>
* Note: The color is resolved immediately, not lazy, because it is passed as parameter to another color function.
* So e.g. {@code darken(lazy(List.background), 10%)} is the same as {@code darken($List.background, 10%)}.
* <p>
* Only useful if a property is defined as lazy and that property is used
* in another property's color function. E.g.
*
* <pre>{@code
* someProperty = lazy(List.background)
* anotherProperty = darken($someProperty, 10%)
* }</pre>
*/
private static Object parseColorLazy( String value, List<String> params, Function<String, String> resolver )
throws IllegalArgumentException
{
if( params.size() != 1 )
throw newMissingParametersException( value );
return parseColorOrFunction( resolver.apply( PROPERTY_PREFIX + params.get( 0 ) ), resolver );
}
/** /**
* Syntax: systemColor(name[,defaultValue]) * Syntax: systemColor(name[,defaultValue])
* - name: system color name * - name: system color name
@@ -974,7 +1001,7 @@ class UIDefaultsLoader
* fadein(color,amount[,options]) or fadeout(color,amount[,options]) * fadein(color,amount[,options]) or fadeout(color,amount[,options])
* - color: a color (e.g. #f00) or a color function * - color: a color (e.g. #f00) or a color function
* - amount: percentage 0-100% * - amount: percentage 0-100%
* - options: [relative] [autoInverse] [noAutoInverse] [lazy] [derived] * - options: [relative] [autoInverse] [noAutoInverse] [derived] [lazy]
*/ */
private static Object parseColorHSLIncreaseDecrease( int hslIndex, boolean increase, private static Object parseColorHSLIncreaseDecrease( int hslIndex, boolean increase,
List<String> params, Function<String, String> resolver ) List<String> params, Function<String, String> resolver )
@@ -984,15 +1011,15 @@ class UIDefaultsLoader
int amount = parsePercentage( params.get( 1 ) ); int amount = parsePercentage( params.get( 1 ) );
boolean relative = false; boolean relative = false;
boolean autoInverse = false; boolean autoInverse = false;
boolean lazy = false;
boolean derived = false; boolean derived = false;
boolean lazy = false;
if( params.size() > 2 ) { if( params.size() > 2 ) {
String options = params.get( 2 ); String options = params.get( 2 );
relative = options.contains( "relative" ); relative = options.contains( "relative" );
autoInverse = options.contains( "autoInverse" ); autoInverse = options.contains( "autoInverse" );
lazy = options.contains( "lazy" );
derived = options.contains( "derived" ); derived = options.contains( "derived" );
lazy = options.contains( "lazy" );
// use autoInverse by default for derived colors, except if noAutoInverse is set // use autoInverse by default for derived colors, except if noAutoInverse is set
if( derived && !options.contains( "noAutoInverse" ) ) if( derived && !options.contains( "noAutoInverse" ) )
@@ -1003,14 +1030,8 @@ class UIDefaultsLoader
ColorFunction function = new ColorFunctions.HSLIncreaseDecrease( ColorFunction function = new ColorFunctions.HSLIncreaseDecrease(
hslIndex, increase, amount, relative, autoInverse ); hslIndex, increase, amount, relative, autoInverse );
if( lazy ) { if( lazy )
return (LazyValue) t -> { return newLazyColorFunction( colorStr, function );
Object color = lazyUIManagerGet( colorStr );
return (color instanceof Color)
? new ColorUIResource( ColorFunctions.applyFunctions( (Color) color, function ) )
: null;
};
}
// parse base color, apply function and create derived color // parse base color, apply function and create derived color
return parseFunctionBaseColor( colorStr, function, derived, resolver ); return parseFunctionBaseColor( colorStr, function, derived, resolver );
@@ -1039,14 +1060,8 @@ class UIDefaultsLoader
// create function // create function
ColorFunction function = new ColorFunctions.Fade( amount ); ColorFunction function = new ColorFunctions.Fade( amount );
if( lazy ) { if( lazy )
return (LazyValue) t -> { return newLazyColorFunction( colorStr, function );
Object color = lazyUIManagerGet( colorStr );
return (color instanceof Color)
? new ColorUIResource( ColorFunctions.applyFunctions( (Color) color, function ) )
: null;
};
}
// parse base color, apply function and create derived color // parse base color, apply function and create derived color
return parseFunctionBaseColor( colorStr, function, derived, resolver ); return parseFunctionBaseColor( colorStr, function, derived, resolver );
@@ -1056,7 +1071,7 @@ class UIDefaultsLoader
* Syntax: spin(color,angle[,options]) * Syntax: spin(color,angle[,options])
* - color: a color (e.g. #f00) or a color function * - color: a color (e.g. #f00) or a color function
* - angle: number of degrees to rotate * - angle: number of degrees to rotate
* - options: [derived] * - options: [derived] [lazy]
*/ */
private static Object parseColorSpin( List<String> params, Function<String, String> resolver ) private static Object parseColorSpin( List<String> params, Function<String, String> resolver )
throws IllegalArgumentException throws IllegalArgumentException
@@ -1064,15 +1079,20 @@ class UIDefaultsLoader
String colorStr = params.get( 0 ); String colorStr = params.get( 0 );
int amount = parseInteger( params.get( 1 ) ); int amount = parseInteger( params.get( 1 ) );
boolean derived = false; boolean derived = false;
boolean lazy = false;
if( params.size() > 2 ) { if( params.size() > 2 ) {
String options = params.get( 2 ); String options = params.get( 2 );
derived = options.contains( "derived" ); derived = options.contains( "derived" );
lazy = options.contains( "lazy" );
} }
// create function // create function
ColorFunction function = new ColorFunctions.HSLIncreaseDecrease( 0, true, amount, false, false ); ColorFunction function = new ColorFunctions.HSLIncreaseDecrease( 0, true, amount, false, false );
if( lazy )
return newLazyColorFunction( colorStr, function );
// parse base color, apply function and create derived color // parse base color, apply function and create derived color
return parseFunctionBaseColor( colorStr, function, derived, resolver ); return parseFunctionBaseColor( colorStr, function, derived, resolver );
} }
@@ -1084,7 +1104,7 @@ class UIDefaultsLoader
* changeAlpha(color,value[,options]) * changeAlpha(color,value[,options])
* - color: a color (e.g. #f00) or a color function * - color: a color (e.g. #f00) or a color function
* - value: for hue: number of degrees; otherwise: percentage 0-100% * - value: for hue: number of degrees; otherwise: percentage 0-100%
* - options: [derived] * - options: [derived] [lazy]
*/ */
private static Object parseColorChange( int hslIndex, private static Object parseColorChange( int hslIndex,
List<String> params, Function<String, String> resolver ) List<String> params, Function<String, String> resolver )
@@ -1095,15 +1115,20 @@ class UIDefaultsLoader
? parseInteger( params.get( 1 ) ) ? parseInteger( params.get( 1 ) )
: parsePercentage( params.get( 1 ) ); : parsePercentage( params.get( 1 ) );
boolean derived = false; boolean derived = false;
boolean lazy = false;
if( params.size() > 2 ) { if( params.size() > 2 ) {
String options = params.get( 2 ); String options = params.get( 2 );
derived = options.contains( "derived" ); derived = options.contains( "derived" );
lazy = options.contains( "lazy" );
} }
// create function // create function
ColorFunction function = new ColorFunctions.HSLChange( hslIndex, value ); ColorFunction function = new ColorFunctions.HSLChange( hslIndex, value );
if( lazy )
return newLazyColorFunction( colorStr, function );
// parse base color, apply function and create derived color // parse base color, apply function and create derived color
return parseFunctionBaseColor( colorStr, function, derived, resolver ); return parseFunctionBaseColor( colorStr, function, derived, resolver );
} }
@@ -1116,7 +1141,7 @@ class UIDefaultsLoader
* - color2: 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 * - 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 * larger weight uses more of first color, smaller weight more of second color
* - options: [derived] * - options: [derived] [lazy]
*/ */
private static Object parseColorMix( String color1Str, List<String> params, Function<String, String> resolver ) private static Object parseColorMix( String color1Str, List<String> params, Function<String, String> resolver )
throws IllegalArgumentException throws IllegalArgumentException
@@ -1127,6 +1152,7 @@ class UIDefaultsLoader
String color2Str = params.get( i++ ); String color2Str = params.get( i++ );
int weight = 50; int weight = 50;
boolean derived = false; boolean derived = false;
boolean lazy = false;
if( params.size() > i ) { if( params.size() > i ) {
String weightStr = params.get( i ); String weightStr = params.get( i );
@@ -1138,6 +1164,7 @@ class UIDefaultsLoader
if( params.size() > i ) { if( params.size() > i ) {
String options = params.get( i ); String options = params.get( i );
derived = options.contains( "derived" ); derived = options.contains( "derived" );
lazy = options.contains( "lazy" );
} }
// parse second color // parse second color
@@ -1148,6 +1175,9 @@ class UIDefaultsLoader
// create function // create function
ColorFunction function = new ColorFunctions.Mix2( color1, weight ); ColorFunction function = new ColorFunctions.Mix2( color1, weight );
if( lazy )
return newLazyColorFunction( color2Str, function );
// parse first color, apply function and create mixed color // parse first color, apply function and create mixed color
return parseFunctionBaseColor( color2Str, function, derived, resolver ); return parseFunctionBaseColor( color2Str, function, derived, resolver );
} }
@@ -1243,6 +1273,15 @@ class UIDefaultsLoader
return new ColorUIResource( newColor ); return new ColorUIResource( newColor );
} }
private static LazyValue newLazyColorFunction( String uiKey, ColorFunction function ) {
return (LazyValue) t -> {
Object color = lazyUIManagerGet( uiKey );
return (color instanceof Color)
? new ColorUIResource( ColorFunctions.applyFunctions( (Color) color, function ) )
: null;
};
}
/** /**
* Syntax: [normal] [bold|+bold|-bold] [italic|+italic|-italic] [<size>|+<incr>|-<decr>|<percent>%] [family[, family]] [$baseFontKey] * Syntax: [normal] [bold|+bold|-bold] [italic|+italic|-italic] [<size>|+<incr>|-<decr>|<percent>%] [family[, family]] [$baseFontKey]
*/ */

View File

@@ -272,9 +272,54 @@ public class TestUIDefaultsLoader
} }
@Test @Test
void parseDerivedColorFunctions() { void parseLazyColorFunctions() {
// lighten, darken // lighten
assertEquals( new Color( 0xff6666 ), parseColorLazy( "lighten(dummyColor, 20%, lazy)", new Color( 0xff0000 ) ) );
// darken
assertEquals( new Color( 0x990000 ), parseColorLazy( "darken(dummyColor, 20%, lazy)", new Color( 0xff0000 ) ) );
// saturate
assertEquals( new Color( 0xf32e2e ), parseColorLazy( "saturate(dummyColor, 20%, lazy)", new Color( 0xdd4444 ) ) );
// desaturate
assertEquals( new Color( 0x745858 ), parseColorLazy( "desaturate(dummyColor, 20%, lazy)", new Color( 0x884444 ) ) );
// fadein
assertEquals( new Color( 0xddff0000, true ), parseColorLazy( "fadein(dummyColor, 20%, lazy)", new Color( 0xaaff0000, true ) ) );
// fadeout
assertEquals( new Color( 0x11ff0000, true ), parseColorLazy( "fadeout(dummyColor, 20%, lazy)", new Color( 0x44ff0000, true ) ) );
// fade
assertEquals( new Color( 0x33ff0000, true ), parseColorLazy( "fade(dummyColor, 20%, lazy)", new Color( 0xff0000 ) ) );
assertEquals( new Color( 0xccff0000, true ), parseColorLazy( "fade(dummyColor, 80%, lazy)", new Color( 0x10ff0000, true ) ) );
// spin
assertEquals( new Color( 0xffaa00 ), parseColorLazy( "spin(dummyColor, 40, lazy)", new Color( 0xff0000 ) ) );
assertEquals( new Color( 0xff00aa ), parseColorLazy( "spin(dummyColor, -40, lazy)", new Color( 0xff0000 ) ) );
// changeHue / changeSaturation / changeLightness / changeAlpha
assertEquals( new Color( 0xffaa00 ), parseColorLazy( "changeHue(dummyColor, 40, lazy)", new Color( 0xff0000 ) ) );
assertEquals( new Color( 0xb34d4d ), parseColorLazy( "changeSaturation(dummyColor, 40%, lazy)", new Color( 0xff0000 ) ) );
assertEquals( new Color( 0xcc0000 ), parseColorLazy( "changeLightness(dummyColor, 40%, lazy)", new Color( 0xff0000 ) ) );
assertEquals( new Color( 0x66ff0000, true ), parseColorLazy( "changeAlpha(dummyColor, 40%, lazy)", new Color( 0xff0000 ) ) );
// mix
assertEquals( new Color( 0x808000 ), parseColorLazy( "mix(#f00, dummyColor, lazy)", new Color( 0x00ff00 ) ) );
assertEquals( new Color( 0xbf4000 ), parseColorLazy( "mix(#f00, dummyColor, 75%, lazy)", new Color( 0x00ff00 ) ) );
// tint
assertEquals( new Color( 0xff80ff ), parseColorLazy( "tint(dummyColor, lazy)", new Color( 0xff00ff ) ) );
assertEquals( new Color( 0xffbfff ), parseColorLazy( "tint(dummyColor, 75%, lazy)", new Color( 0xff00ff ) ) );
// shade
assertEquals( new Color( 0x800080 ), parseColorLazy( "shade(dummyColor, lazy)", new Color( 0xff00ff ) ) );
assertEquals( new Color( 0x400040 ), parseColorLazy( "shade(dummyColor, 75%, lazy)", new Color( 0xff00ff ) ) );
}
@Test
void parseDerivedColorFunctions() {
// mix // mix
assertDerivedColorEquals( new Color( 0x808000 ), "mix(#f00, #0f0, derived)", new Mix2( Color.red, 50 ) ); assertDerivedColorEquals( new Color( 0x808000 ), "mix(#f00, #0f0, derived)", new Mix2( Color.red, 50 ) );
assertDerivedColorEquals( new Color( 0xbf4000 ), "mix(#f00, #0f0, 75%, derived)", new Mix2( Color.red, 75 ) ); assertDerivedColorEquals( new Color( 0xbf4000 ), "mix(#f00, #0f0, 75%, derived)", new Mix2( Color.red, 75 ) );
@@ -400,6 +445,13 @@ public class TestUIDefaultsLoader
return UIDefaultsLoader.parseValue( "dummyColor", value, null ); return UIDefaultsLoader.parseValue( "dummyColor", value, null );
} }
private Object parseColorLazy( String value, Color actual ) {
UIManager.put( "dummyColor", actual );
Object v = UIDefaultsLoader.parseValue( "dummyColor", value, null );
assertInstanceOf( LazyValue.class, v );
return ((LazyValue)v).createValue( null );
}
//---- class TestInstance ------------------------------------------------- //---- class TestInstance -------------------------------------------------
@SuppressWarnings( "EqualsHashCode" ) // Error Prone @SuppressWarnings( "EqualsHashCode" ) // Error Prone