Theme Editor: support color preview for color functions

UIDefaultsLoader: made some private methods package private and return parsed valued type
This commit is contained in:
Karl Tauber
2020-07-07 14:03:39 +02:00
parent 283ba83cef
commit 7ed90cddf8
7 changed files with 242 additions and 17 deletions

View File

@@ -167,7 +167,8 @@ class UIDefaultsLoader
String value = resolveValue( properties, (String) e.getValue() ); String value = resolveValue( properties, (String) e.getValue() );
try { try {
globals.put( key.substring( GLOBAL_PREFIX.length() ), parseValue( key, value, resolver, addonClassLoaders ) ); globals.put( key.substring( GLOBAL_PREFIX.length() ),
parseValue( key, value, null, resolver, addonClassLoaders ) );
} catch( RuntimeException ex ) { } catch( RuntimeException ex ) {
logParseError( Level.SEVERE, key, value, ex ); logParseError( Level.SEVERE, key, value, ex );
} }
@@ -192,7 +193,7 @@ class UIDefaultsLoader
String value = resolveValue( properties, (String) e.getValue() ); String value = resolveValue( properties, (String) e.getValue() );
try { try {
defaults.put( key, parseValue( key, value, resolver, addonClassLoaders ) ); defaults.put( key, parseValue( key, value, null, resolver, addonClassLoaders ) );
} catch( RuntimeException ex ) { } catch( RuntimeException ex ) {
logParseError( Level.SEVERE, key, value, ex ); logParseError( Level.SEVERE, key, value, ex );
} }
@@ -206,7 +207,7 @@ class UIDefaultsLoader
FlatLaf.LOG.log( level, "FlatLaf: Failed to parse: '" + key + '=' + value + '\'', ex ); FlatLaf.LOG.log( level, "FlatLaf: Failed to parse: '" + key + '=' + value + '\'', ex );
} }
private static String resolveValue( Properties properties, String value ) { static String resolveValue( Properties properties, String value ) {
if( value.startsWith( PROPERTY_PREFIX ) ) if( value.startsWith( PROPERTY_PREFIX ) )
value = value.substring( PROPERTY_PREFIX.length() ); value = value.substring( PROPERTY_PREFIX.length() );
else if( !value.startsWith( VARIABLE_PREFIX ) ) else if( !value.startsWith( VARIABLE_PREFIX ) )
@@ -229,26 +230,34 @@ class UIDefaultsLoader
return resolveValue( properties, newValue ); return resolveValue( properties, newValue );
} }
private enum ValueType { UNKNOWN, STRING, CHARACTER, INTEGER, FLOAT, BORDER, ICON, INSETS, DIMENSION, COLOR, enum ValueType { UNKNOWN, STRING, BOOLEAN, CHARACTER, INTEGER, FLOAT, BORDER, ICON, INSETS, DIMENSION, COLOR,
SCALEDINTEGER, SCALEDFLOAT, SCALEDINSETS, SCALEDDIMENSION, INSTANCE, CLASS, GRAYFILTER } SCALEDINTEGER, SCALEDFLOAT, SCALEDINSETS, SCALEDDIMENSION, INSTANCE, CLASS, GRAYFILTER, NULL, LAZY }
private static ValueType[] tempResultValueType = new ValueType[1];
static Object parseValue( String key, String value ) { static Object parseValue( String key, String value ) {
return parseValue( key, value, v -> v, Collections.emptyList() ); return parseValue( key, value, null, v -> v, Collections.emptyList() );
} }
private static Object parseValue( String key, String value, Function<String, String> resolver, List<ClassLoader> addonClassLoaders ) { static Object parseValue( String key, String value, ValueType[] resultValueType,
Function<String, String> resolver, List<ClassLoader> addonClassLoaders )
{
if( resultValueType == null )
resultValueType = tempResultValueType;
value = value.trim(); value = value.trim();
// null, false, true // null, false, true
switch( value ) { switch( value ) {
case "null": return null; case "null": resultValueType[0] = ValueType.NULL; return null;
case "false": return false; case "false": resultValueType[0] = ValueType.BOOLEAN; return false;
case "true": return true; case "true": resultValueType[0] = ValueType.BOOLEAN; return true;
} }
// check for function "lazy" // check for function "lazy"
// Syntax: lazy(uiKey) // Syntax: lazy(uiKey)
if( value.startsWith( "lazy(" ) && value.endsWith( ")" ) ) { if( value.startsWith( "lazy(" ) && value.endsWith( ")" ) ) {
resultValueType[0] = ValueType.LAZY;
String uiKey = value.substring( 5, value.length() - 1 ).trim(); String uiKey = value.substring( 5, value.length() - 1 ).trim();
return (LazyValue) t -> { return (LazyValue) t -> {
return lazyUIManagerGet( uiKey ); return lazyUIManagerGet( uiKey );
@@ -301,6 +310,8 @@ class UIDefaultsLoader
valueType = ValueType.GRAYFILTER; valueType = ValueType.GRAYFILTER;
} }
resultValueType[0] = valueType;
// parse value // parse value
switch( valueType ) { switch( valueType ) {
case STRING: return value; case STRING: return value;
@@ -323,20 +334,27 @@ class UIDefaultsLoader
default: default:
// colors // colors
Object color = parseColorOrFunction( value, resolver, false ); Object color = parseColorOrFunction( value, resolver, false );
if( color != null ) if( color != null ) {
resultValueType[0] = ValueType.COLOR;
return color; return color;
}
// integer // integer
Integer integer = parseInteger( value, false ); Integer integer = parseInteger( value, false );
if( integer != null ) if( integer != null ) {
resultValueType[0] = ValueType.INTEGER;
return integer; return integer;
}
// float // float
Float f = parseFloat( value, false ); Float f = parseFloat( value, false );
if( f != null ) if( f != null ) {
resultValueType[0] = ValueType.FLOAT;
return f; return f;
}
// string // string
resultValueType[0] = ValueType.STRING;
return value; return value;
} }
} }

View File

@@ -21,5 +21,5 @@ plugins {
dependencies { dependencies {
implementation( project( ":flatlaf-core" ) ) implementation( project( ":flatlaf-core" ) )
implementation( "com.fifesoft:rsyntaxtextarea:3.1.0" ) implementation( "com.fifesoft:rsyntaxtextarea:3.1.1" )
} }

View File

@@ -16,6 +16,11 @@
package com.formdev.flatlaf; package com.formdev.flatlaf;
import java.util.Collections;
import java.util.Properties;
import java.util.function.Function;
import com.formdev.flatlaf.UIDefaultsLoader.ValueType;
/** /**
* Enable accessing package private methods of {@link UIDefaultsLoader}. * Enable accessing package private methods of {@link UIDefaultsLoader}.
* *
@@ -23,6 +28,40 @@ package com.formdev.flatlaf;
*/ */
public class UIDefaultsLoaderAccessor public class UIDefaultsLoaderAccessor
{ {
public static Object UNKNOWN = ValueType.UNKNOWN;
public static Object STRING = ValueType.STRING;
public static Object BOOLEAN = ValueType.BOOLEAN;
public static Object CHARACTER = ValueType.CHARACTER;
public static Object INTEGER = ValueType.INTEGER;
public static Object FLOAT = ValueType.FLOAT;
public static Object BORDER = ValueType.BORDER;
public static Object ICON = ValueType.ICON;
public static Object INSETS = ValueType.INSETS;
public static Object DIMENSION = ValueType.DIMENSION;
public static Object COLOR = ValueType.COLOR;
public static Object SCALEDINTEGER = ValueType.SCALEDINTEGER;
public static Object SCALEDFLOAT = ValueType.SCALEDFLOAT;
public static Object SCALEDINSETS = ValueType.SCALEDINSETS;
public static Object SCALEDDIMENSION = ValueType.SCALEDDIMENSION;
public static Object INSTANCE = ValueType.INSTANCE;
public static Object CLASS = ValueType.CLASS;
public static Object GRAYFILTER = ValueType.GRAYFILTER;
public static Object NULL = ValueType.NULL;
public static Object LAZY = ValueType.LAZY;
public static String resolveValue( Properties properties, String value ) {
return UIDefaultsLoader.resolveValue( properties, value );
}
public static Object parseValue( String key, String value, Object[] resultValueType,
Function<String, String> resolver )
{
ValueType[] resultValueType2 = new ValueType[1];
Object result = UIDefaultsLoader.parseValue( key, value, resultValueType2, resolver, Collections.emptyList() );
resultValueType[0] = resultValueType2[0];
return result;
}
public static int parseColorRGBA( String value ) { public static int parseColorRGBA( String value ) {
return UIDefaultsLoader.parseColorRGBA( value ); return UIDefaultsLoader.parseColorRGBA( value );
} }

View File

@@ -34,6 +34,7 @@ class FlatSyntaxTextArea
{ {
private boolean useColorOfColorTokens; private boolean useColorOfColorTokens;
final FlatThemePropertiesSupport propertiesSupport = new FlatThemePropertiesSupport( this );
private final Map<String, Color> parsedColorsMap = new HashMap<>(); private final Map<String, Color> parsedColorsMap = new HashMap<>();
FlatSyntaxTextArea() { FlatSyntaxTextArea() {

View File

@@ -103,11 +103,18 @@ class FlatThemeEditorOverlay
g.setColor( color ); g.setColor( color );
g.fillRect( px, r.y, pw, r.height ); g.fillRect( px, r.y, pw, r.height );
// if color is semi-transparent paint also none-transparent color
int alpha = color.getAlpha();
if( alpha != 255 && pw > r.height * 2 ) {
g.setColor( new Color( color.getRGB() ) );
g.fillRect( px + pw - r.height, r.y, r.height, r.height );
}
// paint text // paint text
int textX = px - maxTextWidth; int textX = px - maxTextWidth;
if( textX > r.x + gap) { if( textX > r.x + gap) {
float[] hsl = HSLColor.fromRGB( color ); float[] hsl = HSLColor.fromRGB( color );
String hslStr = String.format( "HSL %d %d %d", String hslStr = String.format( "HSL %3d %2d %2d",
Math.round( hsl[0] ), Math.round( hsl[1] ), Math.round( hsl[2] ) ); Math.round( hsl[0] ), Math.round( hsl[1] ), Math.round( hsl[2] ) );
g.setColor( textArea.getForeground() ); g.setColor( textArea.getForeground() );
FlatUIUtils.drawString( textArea, g, hslStr, textX, FlatUIUtils.drawString( textArea, g, hslStr, textX,
@@ -120,6 +127,10 @@ class FlatThemeEditorOverlay
} }
private Color getColorInLine( FlatSyntaxTextArea textArea, int line ) { private Color getColorInLine( FlatSyntaxTextArea textArea, int line ) {
Object value = textArea.propertiesSupport.getParsedValueAtLine( line );
if( value instanceof Color )
return (Color) value;
Token token = textArea.getTokenListForLine( line ); Token token = textArea.getTokenListForLine( line );
for( Token t = token; t != null && t.isPaintable(); t = t.getNextToken() ) { for( Token t = token; t != null && t.isPaintable(); t = t.getNextToken() ) {
if( t.getType() == FlatThemeTokenMaker.TOKEN_COLOR ) { if( t.getType() == FlatThemeTokenMaker.TOKEN_COLOR ) {

View File

@@ -0,0 +1,156 @@
/*
* Copyright 2020 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.themeeditor;
import java.awt.Color;
import java.io.IOException;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.function.Function;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import com.formdev.flatlaf.UIDefaultsLoaderAccessor;
/**
* Supports parsing content of text area in FlatLaf properties syntax.
*
* @author Karl Tauber
*/
class FlatThemePropertiesSupport
implements DocumentListener
{
private final FlatSyntaxTextArea textArea;
private final Function<String, String> resolver;
private Properties propertiesCache;
private final Map<Integer, Object> parsedValueCache = new HashMap<>();
FlatThemePropertiesSupport( FlatSyntaxTextArea textArea ) {
this.textArea = textArea;
resolver = v -> {
return resolveValue( v );
};
textArea.getDocument().addDocumentListener( this );
}
private String resolveValue( String value ) {
return UIDefaultsLoaderAccessor.resolveValue( getProperties(), value );
}
Object getParsedValueAtLine( int line ) {
Integer lineKey = line;
Object parsedValue = parsedValueCache.get( lineKey );
if( parsedValue != null )
return !(parsedValue instanceof Exception) ? parsedValue : null;
KeyValue keyValue = getKeyValueAtLine( line );
if( keyValue == null )
return null;
try {
Object[] resultValueType = new Object[1];
String value = resolveValue( keyValue.value );
parsedValue = UIDefaultsLoaderAccessor.parseValue( keyValue.key, value, resultValueType, resolver );
parsedValueCache.put( lineKey, parsedValue );
return parsedValue;
} catch( Exception ex ) {
System.out.println( ex.getMessage() ); //TODO
parsedValueCache.put( lineKey, ex );
return null;
}
}
private KeyValue getKeyValueAtLine( int line ) {
try {
int startOffset = textArea.getLineStartOffset( line );
int endOffset = textArea.getLineEndOffset( line );
String text = textArea.getText( startOffset, endOffset - startOffset );
Properties properties = new Properties();
properties.load( new StringReader( text ) );
if( properties.isEmpty() )
return null;
String key = (String) properties.keys().nextElement();
String value = properties.getProperty( key );
return new KeyValue( key, value );
} catch( BadLocationException | IOException ex ) {
// ignore
return null;
}
}
private Properties getProperties() {
if( propertiesCache != null )
return propertiesCache;
propertiesCache = new Properties();
try {
propertiesCache.load( new StringReader( textArea.getText() ) );
} catch( IOException ex ) {
ex.printStackTrace(); //TODO
}
return propertiesCache;
}
private void clearCache() {
propertiesCache = null;
parsedValueCache.clear();
}
//---- interface DocumentListener ----
@Override
public void insertUpdate( DocumentEvent e ) {
clearCache();
}
@Override
public void removeUpdate( DocumentEvent e ) {
clearCache();
}
@Override
public void changedUpdate( DocumentEvent e ) {
}
//---- class KeyValue -----------------------------------------------------
static class CacheLineInfo {
Object parsedValue;
Object valueType;
Exception parseError;
Color origColor;
}
//---- class KeyValue -----------------------------------------------------
static class KeyValue {
final String key;
final String value;
KeyValue( String key, String value ) {
this.key = key;
this.value = value;
}
}
}

View File

@@ -33,8 +33,8 @@ Prop.lazy=lazy(Prop.string)
Prop.colorFunc1=rgb(12,34,56) Prop.colorFunc1=rgb(12,34,56)
Prop.colorFunc2=rgba(12,34,56,78) Prop.colorFunc2=rgba(12,34,56,78)
Prop.colorFunc3=hsl(12,34,56) Prop.colorFunc3=hsl(12,34%,56%)
Prop.colorFunc4=hsla(12,34,56,78) Prop.colorFunc4=hsla(12,34%,56%,78%)
Prop.colorFunc5=lighten(#fe1289,20%) Prop.colorFunc5=lighten(#fe1289,20%)
Prop.colorFunc6=darken(#fe1289,20%) Prop.colorFunc6=darken(#fe1289,20%)