UIDefaultsLoader: added systemColor() color function that can be used to change accent color (preparation for getting accent color from operating system)

This commit is contained in:
Karl Tauber
2022-06-13 14:59:06 +02:00
parent 9ce7ddd088
commit c7bfd2ea82
8 changed files with 104 additions and 14 deletions

View File

@@ -98,6 +98,7 @@ public abstract class FlatLaf
private static List<Object> customDefaultsSources;
private static Map<String, String> globalExtraDefaults;
private Map<String, String> extraDefaults;
private static Function<String, Color> 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)}.
* <p>
* 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)}.
* <p>
* The keys and values are strings in same format as in FlatLaf properties files.
* <p>
* Sample that setups "FlatLaf Light" theme with red accent color:
* Sample that setups "FlatLaf Light" theme with white background color:
* <pre>{@code
* FlatLaf.setGlobalExtraDefaults( Collections.singletonMap( "@accentColor", "#f00" ) );
* FlatLaf.setGlobalExtraDefaults( Collections.singletonMap( "@background", "#fff" ) );
* FlatLightLaf.setup();
* }</pre>
*
@@ -929,15 +930,15 @@ public abstract class FlatLaf
* E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}.
* <p>
* 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)}.
* <p>
* The keys and values are strings in same format as in FlatLaf properties files.
* <p>
* Sample that setups "FlatLaf Light" theme with red accent color:
* Sample that setups "FlatLaf Light" theme with white background color:
* <pre>{@code
* FlatLaf laf = new FlatLightLaf();
* laf.setExtraDefaults( Collections.singletonMap( "@accentColor", "#f00" ) );
* laf.setExtraDefaults( Collections.singletonMap( "@background", "#fff" ) );
* FlatLaf.setup( laf );
* }</pre>
*
@@ -979,6 +980,36 @@ public abstract class FlatLaf
return val;
}
/**
* Returns the system color getter function, or {@code null}.
*
* @since 3
*/
public static Function<String, Color> getSystemColorGetter() {
return systemColorGetter;
}
/**
* Sets a system color getter function that is invoked when function
* {@code systemColor()} is used in FlatLaf properties files.
* <p>
* The name of the system color is passed as parameter to the function.
* The function should return {@code null} for unknown system colors.
* <p>
* Can be used to change the accent color:
* <pre>{@code
* FlatLaf.setSystemColorGetter( name -> {
* return name.equals( "accent" ) ? Color.red : null;
* } );
* FlatLightLaf.setup();
* }</pre>
*
* @since 3
*/
public static void setSystemColorGetter( Function<String, Color> systemColorGetter ) {
FlatLaf.systemColorGetter = systemColorGetter;
}
private static void reSetLookAndFeel() {
EventQueue.invokeLater( () -> {
LookAndFeel lookAndFeel = UIManager.getLookAndFeel();

View File

@@ -84,6 +84,7 @@ class UIDefaultsLoader
private static int parseColorDepth;
private static Map<String, ColorUIResource> systemColorCache;
private static final SoftCache<String, Object> fontCache = new SoftCache<>();
static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, List<FlatDefaultsAddon> 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<String> params, Function<String, String> 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<String, Color> 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%

View File

@@ -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

View File

@@ -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

View File

@@ -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<? extends LookAndFeel> lafClass = UIManager.getLookAndFeel().getClass();
try {

View File

@@ -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%",

View File

@@ -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 );

View File

@@ -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%)