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 367858c9..4c3f42ea 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java @@ -830,6 +830,7 @@ class UIDefaultsLoader try { switch( function ) { case "if": return parseColorIf( value, params, resolver ); + case "lazy": return parseColorLazy( value, params, resolver ); case "systemColor": return parseColorSystemColor( value, params, resolver ); case "rgb": return parseColorRgbOrRgba( false, params, resolver ); case "rgba": return parseColorRgbOrRgba( true, params, resolver ); @@ -877,6 +878,32 @@ class UIDefaultsLoader return parseColorOrFunction( resolver.apply( ifValue ), resolver ); } + /** + * Syntax: lazy(uiKey) + *

+ * This "lazy" function is only used if the "lazy" is passed as parameter to another + * color function. Otherwise, the general "lazy" function is used. + *

+ * 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%)}. + *

+ * Only useful if a property is defined as lazy and that property is used + * in another property's color function. E.g. + * + *

{@code
+	 * someProperty = lazy(List.background)
+	 * anotherProperty = darken($someProperty, 10%)
+	 * }
+ */ + private static Object parseColorLazy( String value, List params, Function resolver ) + throws IllegalArgumentException + { + if( params.size() != 1 ) + throw newMissingParametersException( value ); + + return parseColorOrFunction( resolver.apply( PROPERTY_PREFIX + params.get( 0 ) ), resolver ); + } + /** * Syntax: systemColor(name[,defaultValue]) * - name: system color name @@ -974,7 +1001,7 @@ class UIDefaultsLoader * fadein(color,amount[,options]) or fadeout(color,amount[,options]) * - color: a color (e.g. #f00) or a color function * - amount: percentage 0-100% - * - options: [relative] [autoInverse] [noAutoInverse] [lazy] [derived] + * - options: [relative] [autoInverse] [noAutoInverse] [derived] [lazy] */ private static Object parseColorHSLIncreaseDecrease( int hslIndex, boolean increase, List params, Function resolver ) @@ -984,15 +1011,15 @@ class UIDefaultsLoader int amount = parsePercentage( params.get( 1 ) ); boolean relative = false; boolean autoInverse = false; - boolean lazy = false; boolean derived = false; + boolean lazy = false; if( params.size() > 2 ) { String options = params.get( 2 ); relative = options.contains( "relative" ); autoInverse = options.contains( "autoInverse" ); - lazy = options.contains( "lazy" ); derived = options.contains( "derived" ); + lazy = options.contains( "lazy" ); // use autoInverse by default for derived colors, except if noAutoInverse is set if( derived && !options.contains( "noAutoInverse" ) ) @@ -1003,14 +1030,8 @@ class UIDefaultsLoader ColorFunction function = new ColorFunctions.HSLIncreaseDecrease( hslIndex, increase, amount, relative, autoInverse ); - if( lazy ) { - return (LazyValue) t -> { - Object color = lazyUIManagerGet( colorStr ); - return (color instanceof Color) - ? new ColorUIResource( ColorFunctions.applyFunctions( (Color) color, function ) ) - : null; - }; - } + if( lazy ) + return newLazyColorFunction( colorStr, function ); // parse base color, apply function and create derived color return parseFunctionBaseColor( colorStr, function, derived, resolver ); @@ -1039,14 +1060,8 @@ class UIDefaultsLoader // create function ColorFunction function = new ColorFunctions.Fade( amount ); - if( lazy ) { - return (LazyValue) t -> { - Object color = lazyUIManagerGet( colorStr ); - return (color instanceof Color) - ? new ColorUIResource( ColorFunctions.applyFunctions( (Color) color, function ) ) - : null; - }; - } + if( lazy ) + return newLazyColorFunction( colorStr, function ); // parse base color, apply function and create derived color return parseFunctionBaseColor( colorStr, function, derived, resolver ); @@ -1056,7 +1071,7 @@ class UIDefaultsLoader * Syntax: spin(color,angle[,options]) * - color: a color (e.g. #f00) or a color function * - angle: number of degrees to rotate - * - options: [derived] + * - options: [derived] [lazy] */ private static Object parseColorSpin( List params, Function resolver ) throws IllegalArgumentException @@ -1064,15 +1079,20 @@ class UIDefaultsLoader String colorStr = params.get( 0 ); int amount = parseInteger( params.get( 1 ) ); boolean derived = false; + boolean lazy = false; if( params.size() > 2 ) { String options = params.get( 2 ); derived = options.contains( "derived" ); + lazy = options.contains( "lazy" ); } // create function 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 return parseFunctionBaseColor( colorStr, function, derived, resolver ); } @@ -1084,7 +1104,7 @@ class UIDefaultsLoader * changeAlpha(color,value[,options]) * - color: a color (e.g. #f00) or a color function * - value: for hue: number of degrees; otherwise: percentage 0-100% - * - options: [derived] + * - options: [derived] [lazy] */ private static Object parseColorChange( int hslIndex, List params, Function resolver ) @@ -1095,15 +1115,20 @@ class UIDefaultsLoader ? parseInteger( params.get( 1 ) ) : parsePercentage( params.get( 1 ) ); boolean derived = false; + boolean lazy = false; if( params.size() > 2 ) { String options = params.get( 2 ); derived = options.contains( "derived" ); + lazy = options.contains( "lazy" ); } // create function ColorFunction function = new ColorFunctions.HSLChange( hslIndex, value ); + if( lazy ) + return newLazyColorFunction( colorStr, function ); + // parse base color, apply function and create derived color return parseFunctionBaseColor( colorStr, function, derived, resolver ); } @@ -1116,7 +1141,7 @@ class UIDefaultsLoader * - 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] + * - options: [derived] [lazy] */ private static Object parseColorMix( String color1Str, List params, Function resolver ) throws IllegalArgumentException @@ -1127,6 +1152,7 @@ class UIDefaultsLoader String color2Str = params.get( i++ ); int weight = 50; boolean derived = false; + boolean lazy = false; if( params.size() > i ) { String weightStr = params.get( i ); @@ -1138,6 +1164,7 @@ class UIDefaultsLoader if( params.size() > i ) { String options = params.get( i ); derived = options.contains( "derived" ); + lazy = options.contains( "lazy" ); } // parse second color @@ -1148,6 +1175,9 @@ class UIDefaultsLoader // create function ColorFunction function = new ColorFunctions.Mix2( color1, weight ); + if( lazy ) + return newLazyColorFunction( color2Str, function ); + // parse first color, apply function and create mixed color return parseFunctionBaseColor( color2Str, function, derived, resolver ); } @@ -1243,6 +1273,15 @@ class UIDefaultsLoader 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] [|+|-|%] [family[, family]] [$baseFontKey] */ diff --git a/flatlaf-core/src/test/java/com/formdev/flatlaf/TestUIDefaultsLoader.java b/flatlaf-core/src/test/java/com/formdev/flatlaf/TestUIDefaultsLoader.java index 73f75419..28c36c89 100644 --- a/flatlaf-core/src/test/java/com/formdev/flatlaf/TestUIDefaultsLoader.java +++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/TestUIDefaultsLoader.java @@ -272,9 +272,54 @@ public class TestUIDefaultsLoader } @Test - void parseDerivedColorFunctions() { - // lighten, darken + void parseLazyColorFunctions() { + // 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 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 ) ); @@ -400,6 +445,13 @@ public class TestUIDefaultsLoader 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 ------------------------------------------------- @SuppressWarnings( "EqualsHashCode" ) // Error Prone