From 974f7d5d6846b4fcf1b30eb4b9aa426c32a11302 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Mon, 11 Nov 2019 18:35:31 +0100 Subject: [PATCH] IntelliJ Themes: reworked applying values so that it works and behaves the same way as in IntelliJ Platform --- .../com/formdev/flatlaf/IntelliJTheme.java | 98 +++++++++++-------- .../com/formdev/flatlaf/UIDefaultsLoader.java | 16 ++- 2 files changed, 67 insertions(+), 47 deletions(-) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/IntelliJTheme.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/IntelliJTheme.java index bc078ac3..3716bfad 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/IntelliJTheme.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/IntelliJTheme.java @@ -16,6 +16,7 @@ package com.formdev.flatlaf; +import java.awt.Color; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -23,9 +24,11 @@ import java.io.Reader; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedHashMap; +import java.util.Enumeration; +import java.util.HashMap; import java.util.Map; import javax.swing.UIDefaults; +import javax.swing.plaf.ColorUIResource; import com.formdev.flatlaf.json.Json; import com.formdev.flatlaf.json.ParseException; @@ -42,6 +45,8 @@ public class IntelliJTheme private final Map ui; private final Map icons; + private Map namedColors = Collections.emptyMap(); + public static boolean install( InputStream in ) throws IOException, ParseException { @@ -86,62 +91,69 @@ public class IntelliJTheme } private void applyProperties( UIDefaults defaults ) { - Map properties = convertToProperties(); - applyProperties( properties, defaults ); + if( ui == null ) + return; + + loadColorPalette( defaults ); + + // convert Json "ui" structure to UI defaults + ArrayList defaultsKeysCache = new ArrayList<>(); + for( Map.Entry e : ui.entrySet() ) + apply( e.getKey(), e.getValue(), defaults, defaultsKeysCache ); } - private static void applyProperties( Map properties, UIDefaults defaults ) { - // globals - ArrayList keys = Collections.list( defaults.keys() ); - for( Map.Entry e : properties.entrySet() ) { - String key = e.getKey(); - if( !key.startsWith( "*." ) ) - continue; + private void loadColorPalette( UIDefaults defaults ) { + if( colors == null ) + return; - String tail = key.substring( 1 ); - Object uiValue = UIDefaultsLoader.parseValue( key, e.getValue() ); - for( Object k : keys ) { - if( k instanceof String && ((String)k).endsWith( tail ) ) - defaults.put( k, uiValue ); + namedColors = new HashMap<>(); + + for( Map.Entry e : colors.entrySet() ) { + String value = e.getValue(); + ColorUIResource color = UIDefaultsLoader.parseColor( value ); + if( color != null ) { + String key = e.getKey(); + namedColors.put( key, color ); + defaults.put( "ColorPalette." + e.getKey(), color ); } } - - // non-globals - for( Map.Entry e : properties.entrySet() ) { - String key = e.getKey(); - if( key.startsWith( "*." ) ) - continue; - - Object uiValue = UIDefaultsLoader.parseValue( key, e.getValue() ); - defaults.put( key, uiValue ); - } - } - - private Map convertToProperties() { - Map properties = new LinkedHashMap<>(); - if( ui == null ) - return properties; - - // convert json structure to properties map - for( Map.Entry e : ui.entrySet() ) - addToProperties( e.getKey(), e.getValue(), properties ); - - return properties; } @SuppressWarnings( "unchecked" ) - private void addToProperties( String key, Object value, Map properties ) { + private void apply( String key, Object value, UIDefaults defaults, ArrayList defaultsKeysCache ) { if( value instanceof Map ) { for( Map.Entry e : ((Map)value).entrySet() ) - addToProperties( key + '.' + e.getKey(), e.getValue(), properties ); + apply( key + '.' + e.getKey(), e.getValue(), defaults, defaultsKeysCache ); } else { String valueStr = value.toString(); - // map colors - if( colors != null ) - valueStr = colors.getOrDefault( valueStr, valueStr ); + // map named colors + Object uiValue = namedColors.get( valueStr ); - properties.put( key, valueStr ); + // parse value + if( uiValue == null ) + uiValue = UIDefaultsLoader.parseValue( key, valueStr ); + + if( key.startsWith( "*." ) ) { + // wildcard + String tail = key.substring( 1 ); + + // because we can not iterate over the UI defaults keys while + // modifying UI defaults in the same loop, we have to copy the keys + if( defaultsKeysCache.size() != defaults.size() ) { + defaultsKeysCache.clear(); + Enumeration e = defaults.keys(); + while( e.hasMoreElements() ) + defaultsKeysCache.add( e.nextElement() ); + } + + // replace all values in UI defaults that match the wildcard key + for( Object k : defaultsKeysCache ) { + if( k instanceof String && ((String)k).endsWith( tail ) ) + defaults.put( k, uiValue ); + } + } else + defaults.put( key, uiValue ); } } 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 acfc75d1..3777581b 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java @@ -229,12 +229,12 @@ class UIDefaultsLoader case ICON: return parseInstance( value ); case INSETS: return parseInsets( value ); case SIZE: return parseSize( value ); - case COLOR: return parseColor( value, true ); + case COLOR: return parseColorOrFunction( value, true ); case SCALEDNUMBER: return parseScaledNumber( value ); case UNKNOWN: default: // colors - ColorUIResource color = parseColor( value, false ); + ColorUIResource color = parseColorOrFunction( value, false ); if( color != null ) return color; @@ -254,7 +254,7 @@ class UIDefaultsLoader List parts = split( value, ',' ); Insets insets = parseInsets( value ); ColorUIResource lineColor = (parts.size() == 5) - ? parseColor( resolver.apply( parts.get( 4 ) ), true ) + ? parseColorOrFunction( resolver.apply( parts.get( 4 ) ), true ) : null; return (LazyValue) t -> { @@ -301,10 +301,18 @@ class UIDefaultsLoader } } - private static ColorUIResource parseColor( String value, boolean reportError ) { + private static ColorUIResource parseColorOrFunction( String value, boolean reportError ) { if( value.endsWith( ")" ) ) return parseColorFunctions( value, reportError ); + return parseColor( value, reportError ); + } + + static ColorUIResource parseColor( String value ) { + return parseColor( value, false ); + } + + private static ColorUIResource parseColor( String value, boolean reportError ) { if( value.startsWith( "#" ) ) value = value.substring( 1 );