UIDefaultsLoader: added if() function (inspired by Less CSS)

This commit is contained in:
Karl Tauber
2021-09-26 23:54:06 +02:00
parent 36d5747fbf
commit a4ea88f4be
4 changed files with 83 additions and 3 deletions

View File

@@ -312,6 +312,24 @@ class UIDefaultsLoader
case "true": resultValueType[0] = ValueType.BOOLEAN; return true;
}
// check for function "if"
// Syntax: if(condition,trueValue,falseValue)
// - condition: evaluates to true if:
// - is not "null"
// - is not "false"
// - is not an integer with zero value
// - trueValue: used if condition is true
// - falseValue: used if condition is false
if( value.startsWith( "if(" ) && value.endsWith( ")" ) ) {
List<String> params = splitFunctionParams( value.substring( 3, value.length() - 1 ), ',' );
if( params.size() != 3 )
throwMissingParametersException( value );
boolean ifCondition = parseCondition( params.get( 0 ), resolver, addonClassLoaders );
String ifValue = params.get( ifCondition ? 1 : 2 );
return parseValue( key, resolver.apply( ifValue ), resultValueType, resolver, addonClassLoaders );
}
// check for function "lazy"
// Syntax: lazy(uiKey)
if( value.startsWith( "lazy(" ) && value.endsWith( ")" ) ) {
@@ -420,6 +438,20 @@ class UIDefaultsLoader
}
}
private static boolean parseCondition( String condition,
Function<String, String> resolver, List<ClassLoader> addonClassLoaders )
{
try {
Object conditionValue = parseValue( "", resolver.apply( condition ), null, resolver, addonClassLoaders );
return (conditionValue != null &&
!conditionValue.equals( false ) &&
!conditionValue.equals( 0 ) );
} catch( IllegalArgumentException ex ) {
// ignore errors (e.g. variable or property not found) and evaluate to false
return false;
}
}
private static Object parseBorder( String value, Function<String, String> resolver, List<ClassLoader> addonClassLoaders ) {
if( value.indexOf( ',' ) >= 0 ) {
// top,left,bottom,right[,lineColor[,lineThickness]]
@@ -584,7 +616,7 @@ class UIDefaultsLoader
String function = value.substring( 0, paramsStart ).trim();
List<String> params = splitFunctionParams( value.substring( paramsStart + 1, value.length() - 1 ), ',' );
if( params.isEmpty() )
throw new IllegalArgumentException( "missing parameters in function '" + value + "'" );
throwMissingParametersException( value );
if( parseColorDepth > 100 )
throw new IllegalArgumentException( "endless recursion in color function '" + value + "'" );
@@ -592,6 +624,7 @@ class UIDefaultsLoader
parseColorDepth++;
try {
switch( function ) {
case "if": return parseColorIf( value, params, resolver, reportError );
case "rgb": return parseColorRgbOrRgba( false, params, resolver, reportError );
case "rgba": return parseColorRgbOrRgba( true, params, resolver, reportError );
case "hsl": return parseColorHslOrHsla( false, params );
@@ -620,6 +653,21 @@ class UIDefaultsLoader
throw new IllegalArgumentException( "unknown color function '" + value + "'" );
}
/**
* Syntax: if(condition,trueValue,falseValue)
* <p>
* This "if" function is only used if the "if" is passed as parameter to another
* color function. Otherwise the general "if" function is used.
*/
private static Object parseColorIf( String value, List<String> params, Function<String, String> resolver, boolean reportError ) {
if( params.size() != 3 )
throwMissingParametersException( value );
boolean ifCondition = parseCondition( params.get( 0 ), resolver, Collections.emptyList() );
String ifValue = params.get( ifCondition ? 1 : 2 );
return parseColorOrFunction( resolver.apply( ifValue ), resolver, reportError );
}
/**
* Syntax: rgb(red,green,blue) or rgba(red,green,blue,alpha)
* - red: an integer 0-255 or a percentage 0-100%
@@ -1029,4 +1077,8 @@ class UIDefaultsLoader
LoggingFacade.INSTANCE.logSevere( "FlatLaf: '" + uiKey + "' not found in UI defaults.", null );
return value;
}
private static void throwMissingParametersException( String value ) {
throw new IllegalArgumentException( "missing parameters in function '" + value + "'" );
}
}

View File

@@ -405,6 +405,13 @@ class FlatCompletionProvider
setParameterizedCompletionParams( '(', ",", ')' );
setParameterChoicesProvider( this );
addFunction( "if",
"condition", "evaluates to true if: is not \"null\" and is not \"false\" and is not an integer with zero value",
"trueValue", "used if condition is true",
"falseValue", "used if condition is false" );
addFunction( "lazy",
"uiKey", "UI key (without leading '$')" );
addFunction( "rgb",
"red", "0-255 or 0-100%",
"green", "0-255 or 0-100%",

View File

@@ -51,7 +51,11 @@ public class FlatThemeTokenMaker
tokenMap.put( "false", Token.LITERAL_BOOLEAN );
tokenMap.put( "true", Token.LITERAL_BOOLEAN );
// functions
// general functions
tokenMap.put( "if", TOKEN_FUNCTION );
tokenMap.put( "lazy", TOKEN_FUNCTION );
// color functions
tokenMap.put( "rgb", TOKEN_FUNCTION );
tokenMap.put( "rgba", TOKEN_FUNCTION );
tokenMap.put( "hsl", TOKEN_FUNCTION );
@@ -72,7 +76,6 @@ public class FlatThemeTokenMaker
tokenMap.put( "tint", TOKEN_FUNCTION );
tokenMap.put( "shade", TOKEN_FUNCTION );
tokenMap.put( "contrast", TOKEN_FUNCTION );
tokenMap.put( "lazy", TOKEN_FUNCTION );
// function options
tokenMap.put( "relative", Token.RESERVED_WORD );

View File

@@ -29,6 +29,24 @@ Prop.true = true
Prop.var = @var1
Prop.ref = $Prop.string
Prop.ifNotNull = if(#000,#0f0,#dfd)
Prop.ifNull = if(null,#0f0,#dfd)
Prop.ifTrue = if(true,#0f0,#dfd)
Prop.ifFalse = if(false,#0f0,#dfd)
Prop.ifOne = if(1,#0f0,#dfd)
Prop.ifZero = if(0,#0f0,#dfd)
@varTrue = true
@varFalse = false
@varTrueValue = #0f0
@varFalseValue = #dfd
Prop.ifVarTrue = if(@varTrue,@varTrueValue,@varFalseValue)
Prop.ifVarFalse = if(@varFalse,@varTrueValue,@varFalseValue)
Prop.ifTrueColorFunc = if(true,lighten(#f00,20%),darken(#f00,20%))
Prop.ifFalseColorFunc = if(false,lighten(#f00,20%),darken(#f00,20%))
Prop.ifUndefinedVar = if(@undefinedVar,#0f0,#dfd)
Prop.ifUndefinedProp = if($undefinedProp,#0f0,#dfd)
Prop.ifColor = lighten(if(#000,#0f0,#dfd), 10%)
Prop.ifColorVar = lighten(if(@varTrue,@varTrueValue,@varFalseValue), 10%)
Prop.lazy = lazy(Prop.string)
Prop.colorFunc1 = rgb(12,34,56)