From c7bfd2ea82b1ce39a05464e2fb1b6c3b7a712642 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Mon, 13 Jun 2022 14:59:06 +0200 Subject: [PATCH] UIDefaultsLoader: added systemColor() color function that can be used to change accent color (preparation for getting accent color from operating system) --- .../java/com/formdev/flatlaf/FlatLaf.java | 43 ++++++++++++++--- .../com/formdev/flatlaf/UIDefaultsLoader.java | 47 +++++++++++++++++++ .../formdev/flatlaf/FlatDarkLaf.properties | 2 +- .../formdev/flatlaf/FlatLightLaf.properties | 2 +- .../com/formdev/flatlaf/demo/DemoFrame.java | 16 ++++--- .../themeeditor/FlatCompletionProvider.java | 4 ++ .../themeeditor/FlatThemeTokenMaker.java | 1 + .../theme-editor-test.properties | 3 ++ 8 files changed, 104 insertions(+), 14 deletions(-) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java index dde7fa14..8abd2ad3 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java @@ -98,6 +98,7 @@ public abstract class FlatLaf private static List customDefaultsSources; private static Map globalExtraDefaults; private Map extraDefaults; + private static Function systemColorGetter; private String desktopPropertyName; private String desktopPropertyName2; @@ -897,14 +898,14 @@ public abstract class FlatLaf * E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}. *

* The global extra defaults are useful for smaller additional defaults that may change. - * E.g. accent color. Otherwise, FlatLaf properties files should be used. + * Otherwise, FlatLaf properties files should be used. * See {@link #registerCustomDefaultsSource(String)}. *

* The keys and values are strings in same format as in FlatLaf properties files. *

- * Sample that setups "FlatLaf Light" theme with red accent color: + * Sample that setups "FlatLaf Light" theme with white background color: *

{@code
-	 * FlatLaf.setGlobalExtraDefaults( Collections.singletonMap( "@accentColor", "#f00" ) );
+	 * FlatLaf.setGlobalExtraDefaults( Collections.singletonMap( "@background", "#fff" ) );
 	 * FlatLightLaf.setup();
 	 * }
* @@ -929,15 +930,15 @@ public abstract class FlatLaf * E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}. *

* The extra defaults are useful for smaller additional defaults that may change. - * E.g. accent color. Otherwise, FlatLaf properties files should be used. + * Otherwise, FlatLaf properties files should be used. * See {@link #registerCustomDefaultsSource(String)}. *

* The keys and values are strings in same format as in FlatLaf properties files. *

- * Sample that setups "FlatLaf Light" theme with red accent color: + * Sample that setups "FlatLaf Light" theme with white background color: *

{@code
 	 * FlatLaf laf = new FlatLightLaf();
-	 * laf.setExtraDefaults( Collections.singletonMap( "@accentColor", "#f00" ) );
+	 * laf.setExtraDefaults( Collections.singletonMap( "@background", "#fff" ) );
 	 * FlatLaf.setup( laf );
 	 * }
* @@ -979,6 +980,36 @@ public abstract class FlatLaf return val; } + /** + * Returns the system color getter function, or {@code null}. + * + * @since 3 + */ + public static Function getSystemColorGetter() { + return systemColorGetter; + } + + /** + * Sets a system color getter function that is invoked when function + * {@code systemColor()} is used in FlatLaf properties files. + *

+ * The name of the system color is passed as parameter to the function. + * The function should return {@code null} for unknown system colors. + *

+ * Can be used to change the accent color: + *

{@code
+	 * FlatLaf.setSystemColorGetter( name -> {
+	 *     return name.equals( "accent" ) ? Color.red : null;
+	 * } );
+	 * FlatLightLaf.setup();
+	 * }
+ * + * @since 3 + */ + public static void setSystemColorGetter( Function systemColorGetter ) { + FlatLaf.systemColorGetter = systemColorGetter; + } + private static void reSetLookAndFeel() { EventQueue.invokeLater( () -> { LookAndFeel lookAndFeel = UIManager.getLookAndFeel(); 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 8603c2f8..06c0e726 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java @@ -84,6 +84,7 @@ class UIDefaultsLoader private static int parseColorDepth; + private static Map systemColorCache; private static final SoftCache fontCache = new SoftCache<>(); static void loadDefaultsFromProperties( Class lookAndFeelClass, List addons, @@ -105,6 +106,10 @@ class UIDefaultsLoader Properties additionalDefaults, boolean dark, UIDefaults defaults ) { try { + // temporary cache system colors while loading defaults, + // which avoids that system color getter is invoked multiple times + systemColorCache = (FlatLaf.getSystemColorGetter() != null) ? new HashMap<>() : null; + // load core properties files Properties properties = new Properties(); for( Class lafClass : lafClasses ) { @@ -276,6 +281,9 @@ class UIDefaultsLoader // remember variables in defaults to allow using them in styles defaults.put( KEY_VARIABLES, variables ); + + // clear/disable system color cache + systemColorCache = null; } catch( IOException ex ) { LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to load properties files.", ex ); } @@ -769,6 +777,7 @@ class UIDefaultsLoader try { switch( function ) { case "if": return parseColorIf( value, params, resolver ); + case "systemColor": return parseColorSystemColor( value, params, resolver ); case "rgb": return parseColorRgbOrRgba( false, params, resolver ); case "rgba": return parseColorRgbOrRgba( true, params, resolver ); case "hsl": return parseColorHslOrHsla( false, params ); @@ -813,6 +822,44 @@ class UIDefaultsLoader return parseColorOrFunction( resolver.apply( ifValue ), resolver ); } + /** + * Syntax: systemColor(name[,defaultValue]) + * - name: system color name + * - defaultValue: default color value used if system color is not available + */ + private static Object parseColorSystemColor( String value, List params, Function resolver ) { + if( params.size() < 1 ) + throwMissingParametersException( value ); + + ColorUIResource systemColor = getSystemColor( params.get( 0 ) ); + if( systemColor != null ) + return systemColor; + + String defaultValue = (params.size() > 1) ? params.get( 1 ) : ""; + if( defaultValue.equals( "null" ) || defaultValue.isEmpty() ) + return null; + + return parseColorOrFunction( resolver.apply( defaultValue ), resolver ); + } + + private static ColorUIResource getSystemColor( String name ) { + Function systemColorGetter = FlatLaf.getSystemColorGetter(); + if( systemColorGetter == null ) + return null; + + // use containsKey() because value may be null + if( systemColorCache != null && systemColorCache.containsKey( name ) ) + return systemColorCache.get( name ); + + Color color = systemColorGetter.apply( name ); + ColorUIResource uiColor = (color != null) ? new ColorUIResource( color ) : null; + + if( systemColorCache != null ) + systemColorCache.put( name, uiColor ); + + return uiColor; + } + /** * Syntax: rgb(red,green,blue) or rgba(red,green,blue,alpha) * - red: an integer 0-255 or a percentage 0-100% diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatDarkLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatDarkLaf.properties index 7ef63e19..4fe07ee4 100644 --- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatDarkLaf.properties +++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatDarkLaf.properties @@ -63,7 +63,7 @@ # accent colors (blueish) # set @accentColor to use single accent color or # modify @accentBaseColor to use variations of accent base color -@accentColor = null +@accentColor = systemColor(accent,null) @accentBaseColor = #4B6EAF @accentBase2Color = lighten(saturate(spin(@accentBaseColor,-8),13%),5%) # accent color variations diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLightLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLightLaf.properties index cad23984..98b064e4 100644 --- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLightLaf.properties +++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLightLaf.properties @@ -63,7 +63,7 @@ # accent colors (blueish) # set @accentColor to use single accent color or # modify @accentBaseColor to use variations of accent base color -@accentColor = null +@accentColor = systemColor(accent,null) @accentBaseColor = #2675BF @accentBase2Color = lighten(saturate(@accentBaseColor,10%),6%) # accent color variations diff --git a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java index be4bf174..ed1a9a44 100644 --- a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java +++ b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java @@ -24,7 +24,6 @@ import java.net.URISyntaxException; import java.time.Year; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.prefs.Preferences; import javax.swing.*; import javax.swing.text.DefaultEditorKit; @@ -398,6 +397,7 @@ class DemoFrame }; private final JToggleButton[] accentColorButtons = new JToggleButton[accentColorKeys.length]; private JLabel accentColorLabel; + private Color accentColor; private void initAccentColors() { accentColorLabel = new JLabel( "Accent color: " ); @@ -416,6 +416,10 @@ class DemoFrame accentColorButtons[0].setSelected( true ); + FlatLaf.setSystemColorGetter( name -> { + return name.equals( "accent" ) ? accentColor : null; + } ); + UIManager.addPropertyChangeListener( e -> { if( "lookAndFeel".equals( e.getPropertyName() ) ) updateAccentColorButtons(); @@ -424,17 +428,17 @@ class DemoFrame } private void accentColorChanged( ActionEvent e ) { - String accentColor = accentColorKeys[0]; + String accentColorKey = null; for( int i = 0; i < accentColorButtons.length; i++ ) { if( accentColorButtons[i].isSelected() ) { - accentColor = accentColorKeys[i]; + accentColorKey = accentColorKeys[i]; break; } } - FlatLaf.setGlobalExtraDefaults( (accentColor != accentColorKeys[0]) - ? Collections.singletonMap( "@accentColor", "$" + accentColor ) - : null ); + accentColor = (accentColorKey != null && accentColorKey != accentColorKeys[0]) + ? UIManager.getColor( accentColorKey ) + : null; Class lafClass = UIManager.getLookAndFeel().getClass(); try { diff --git a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatCompletionProvider.java b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatCompletionProvider.java index 71959f4c..164e8a89 100644 --- a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatCompletionProvider.java +++ b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatCompletionProvider.java @@ -410,6 +410,10 @@ class FlatCompletionProvider addFunction( "lazy", "uiKey", "UI key (without leading '$')" ); + addFunction( "systemColor", + "name", "system color name", + "defaultValue", "default color value used if system color is not available" ); + addFunction( "rgb", "red", "0-255 or 0-100%", "green", "0-255 or 0-100%", diff --git a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeTokenMaker.java b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeTokenMaker.java index f9bbe12b..aed8b3a6 100644 --- a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeTokenMaker.java +++ b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeTokenMaker.java @@ -56,6 +56,7 @@ public class FlatThemeTokenMaker tokenMap.put( "lazy", TOKEN_FUNCTION ); // color functions + tokenMap.put( "systemColor", TOKEN_FUNCTION ); tokenMap.put( "rgb", TOKEN_FUNCTION ); tokenMap.put( "rgba", TOKEN_FUNCTION ); tokenMap.put( "hsl", TOKEN_FUNCTION ); diff --git a/flatlaf-theme-editor/theme-editor-test.properties b/flatlaf-theme-editor/theme-editor-test.properties index 50d46d2a..eafb7b50 100644 --- a/flatlaf-theme-editor/theme-editor-test.properties +++ b/flatlaf-theme-editor/theme-editor-test.properties @@ -49,6 +49,9 @@ Prop.ifColor = lighten(if(#000,#0f0,#dfd), 10%) Prop.ifColorVar = lighten(if(@varTrue,@varTrueValue,@varFalseValue), 10%) Prop.lazy = lazy(Prop.string) +Prop.systemColor1 = systemColor(accent,null) +Prop.systemColor2 = systemColor(accent,#f00) + Prop.colorFunc1 = rgb(12,34,56) Prop.colorFunc2 = rgba(12,34,56,78) Prop.colorFunc3 = hsl(12,34%,56%)