diff --git a/CHANGELOG.md b/CHANGELOG.md
index e65348fc..5bbfbcdb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,11 @@ FlatLaf Change Log
## 2.0-SNAPSHOT
+#### New features and improvements
+
+- Styling components using string in CSS syntax or `java.util.Map`. (PR #341)\
+ For example: `mySlider.putClientProperty( "FlatLaf.style", "trackWidth: 2" );`
+
#### Fixed bugs
- Tree: Fixed editing cell issue with custom cell renderer and cell editor that
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java
index dace152f..0d83eb42 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java
@@ -126,6 +126,25 @@ public interface FlatClientProperties
//---- JComponent ---------------------------------------------------------
+ /**
+ * Specifies the style of a component as String in CSS syntax ("key1: value1; key2: value2; ...")
+ * or as {@link java.util.Map}<String, Object> with binary values.
+ *
+ * The keys are the same as used in UI defaults, but without component type prefix.
+ * E.g. for UI default {@code Slider.thumbSize} use key {@code thumbSize}.
+ *
+ * The syntax of the CSS values is the same as used in FlatLaf properties files
+ * (https://www.formdev.com/flatlaf/properties-files/ ),
+ * but some features are not supported (e.g. variables).
+ * When using a map, the values are not parsed from a string. They must be binary.
+ *
+ * Components {@link javax.swing.JComponent}
+ * Value type {@link java.lang.String} or {@link java.util.Map}<String, Object>
+ *
+ * @since 2
+ */
+ String STYLE = "FlatLaf.style";
+
/**
* Specifies minimum width of a component.
*
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 9ff1ff0a..d9589d19 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java
@@ -32,6 +32,7 @@ import java.beans.PropertyChangeListener;
import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
@@ -55,6 +56,7 @@ import javax.swing.RootPaneContainer;
import javax.swing.SwingUtilities;
import javax.swing.UIDefaults;
import javax.swing.UIDefaults.ActiveValue;
+import javax.swing.UIDefaults.LazyValue;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.plaf.ColorUIResource;
@@ -779,6 +781,34 @@ public abstract class FlatLaf
customDefaultsSources.remove( folder );
}
+ /**
+ * Parses a UI defaults value string and converts it into a binary object.
+ *
+ * See: https://www.formdev.com/flatlaf/properties-files/
+ *
+ * @param key the key, which is used to determine the value type if parameter {@code valueType} is {@code null}
+ * @param value the value string
+ * @param valueType the expected value type, or {@code null}
+ * @return the binary value
+ * @throws IllegalArgumentException on syntax errors
+ * @since 2
+ */
+ public static Object parseDefaultsValue( String key, String value, Class> valueType )
+ throws IllegalArgumentException
+ {
+ // parse value
+ Object val = UIDefaultsLoader.parseValue( key, value, valueType, null,
+ v -> UIDefaultsLoader.resolveValueFromUIManager( v ), Collections.emptyList() );
+
+ // create actual value if lazy or active
+ if( val instanceof LazyValue )
+ val = ((LazyValue)val).createValue( null );
+ else if( val instanceof ActiveValue )
+ val = ((ActiveValue)val).createValue( null );
+
+ return val;
+ }
+
private static void reSetLookAndFeel() {
EventQueue.invokeLater( () -> {
LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
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 5a2fa716..554f7d2b 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/IntelliJTheme.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/IntelliJTheme.java
@@ -338,7 +338,7 @@ public class IntelliJTheme
// parse value
try {
- uiValue = UIDefaultsLoader.parseValue( key, valueStr );
+ uiValue = UIDefaultsLoader.parseValue( key, valueStr, null );
} catch( RuntimeException ex ) {
UIDefaultsLoader.logParseError( key, valueStr, ex, false );
return; // ignore invalid value
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 1b6d1ab3..0edf48e4 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java
@@ -33,8 +33,10 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.function.Function;
+import javax.swing.Icon;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
+import javax.swing.border.Border;
import javax.swing.UIDefaults.ActiveValue;
import javax.swing.UIDefaults.LazyValue;
import javax.swing.plaf.ColorUIResource;
@@ -241,7 +243,7 @@ class UIDefaultsLoader
String value = resolveValue( (String) e.getValue(), propertiesGetter );
try {
- defaults.put( key, parseValue( key, value, null, resolver, addonClassLoaders ) );
+ defaults.put( key, parseValue( key, value, null, null, resolver, addonClassLoaders ) );
} catch( RuntimeException ex ) {
logParseError( key, value, ex, true );
}
@@ -288,16 +290,36 @@ class UIDefaultsLoader
return resolveValue( newValue, propertiesGetter );
}
+ static String resolveValueFromUIManager( String value ) {
+ if( !value.startsWith( PROPERTY_PREFIX ) )
+ return value;
+
+ String key = value.substring( PROPERTY_PREFIX.length() );
+ Object newValue = UIManager.get( key );
+ if( newValue == null )
+ throw new IllegalArgumentException( "property '" + key + "' not found" );
+
+ // convert binary color to string
+ if( newValue instanceof Color ) {
+ Color color = (Color) newValue;
+ int alpha = color.getAlpha();
+ return String.format( (alpha != 255) ? "#%06x%02x" : "#%06x", color.getRGB() & 0xffffff, alpha );
+ }
+
+ throw new IllegalArgumentException( "property value type '" + newValue.getClass().getName() + "' not supported in references" );
+ }
+
enum ValueType { UNKNOWN, STRING, BOOLEAN, CHARACTER, INTEGER, FLOAT, BORDER, ICON, INSETS, DIMENSION, COLOR,
SCALEDINTEGER, SCALEDFLOAT, SCALEDINSETS, SCALEDDIMENSION, INSTANCE, CLASS, GRAYFILTER, NULL, LAZY }
private static ValueType[] tempResultValueType = new ValueType[1];
+ private static Map, ValueType> javaValueTypes;
- static Object parseValue( String key, String value ) {
- return parseValue( key, value, null, v -> v, Collections.emptyList() );
+ static Object parseValue( String key, String value, Class> valueType ) {
+ return parseValue( key, value, valueType, null, v -> v, Collections.emptyList() );
}
- static Object parseValue( String key, String value, ValueType[] resultValueType,
+ static Object parseValue( String key, String value, Class> javaValueType, ValueType[] resultValueType,
Function resolver, List addonClassLoaders )
{
if( resultValueType == null )
@@ -305,70 +327,106 @@ class UIDefaultsLoader
value = value.trim();
- // null, false, true
- switch( value ) {
- case "null": resultValueType[0] = ValueType.NULL; return null;
- case "false": resultValueType[0] = ValueType.BOOLEAN; return false;
- case "true": resultValueType[0] = ValueType.BOOLEAN; return true;
- }
-
- // check for function "lazy"
- // Syntax: lazy(uiKey)
- if( value.startsWith( "lazy(" ) && value.endsWith( ")" ) ) {
- resultValueType[0] = ValueType.LAZY;
- String uiKey = value.substring( 5, value.length() - 1 ).trim();
- return (LazyValue) t -> {
- return lazyUIManagerGet( uiKey );
- };
+ // null
+ if( value.equals( "null" ) ) {
+ resultValueType[0] = ValueType.NULL;
+ return null;
}
ValueType valueType = ValueType.UNKNOWN;
- // check whether value type is specified in the value
- if( value.startsWith( "#" ) )
- valueType = ValueType.COLOR;
- else if( value.startsWith( "\"" ) && value.endsWith( "\"" ) ) {
- valueType = ValueType.STRING;
- value = value.substring( 1, value.length() - 1 );
- } else if( value.startsWith( TYPE_PREFIX ) ) {
- int end = value.indexOf( TYPE_PREFIX_END );
- if( end != -1 ) {
- try {
- String typeStr = value.substring( TYPE_PREFIX.length(), end );
- valueType = ValueType.valueOf( typeStr.toUpperCase( Locale.ENGLISH ) );
+ if( javaValueType != null ) {
+ if( javaValueTypes == null ) {
+ // create lazy
+ javaValueTypes = new HashMap<>();
+ javaValueTypes.put( String.class, ValueType.STRING );
+ javaValueTypes.put( boolean.class, ValueType.BOOLEAN );
+ javaValueTypes.put( Boolean.class, ValueType.BOOLEAN );
+ javaValueTypes.put( char.class, ValueType.CHARACTER );
+ javaValueTypes.put( Character.class, ValueType.CHARACTER );
+ javaValueTypes.put( int.class, ValueType.INTEGER );
+ javaValueTypes.put( Integer.class, ValueType.INTEGER );
+ javaValueTypes.put( float.class, ValueType.FLOAT );
+ javaValueTypes.put( Float.class, ValueType.FLOAT );
+ javaValueTypes.put( Border.class, ValueType.BORDER );
+ javaValueTypes.put( Icon.class, ValueType.ICON );
+ javaValueTypes.put( Insets.class, ValueType.INSETS );
+ javaValueTypes.put( Dimension.class, ValueType.DIMENSION );
+ javaValueTypes.put( Color.class, ValueType.COLOR );
+ }
- // remove type from value
- value = value.substring( end + TYPE_PREFIX_END.length() );
- } catch( IllegalArgumentException ex ) {
- // ignore
+ // map java value type to parser value type
+ valueType = javaValueTypes.get( javaValueType );
+ if( valueType == null )
+ throw new IllegalArgumentException( "unsupported value type '" + javaValueType.getName() + "'" );
+
+ // remove '"' from strings
+ if( valueType == ValueType.STRING && value.startsWith( "\"" ) && value.endsWith( "\"" ) )
+ value = value.substring( 1, value.length() - 1 );
+ } else {
+ // false, true
+ switch( value ) {
+ case "false": resultValueType[0] = ValueType.BOOLEAN; return false;
+ case "true": resultValueType[0] = ValueType.BOOLEAN; return true;
+ }
+
+ // check for function "lazy"
+ // Syntax: lazy(uiKey)
+ if( value.startsWith( "lazy(" ) && value.endsWith( ")" ) ) {
+ resultValueType[0] = ValueType.LAZY;
+ String uiKey = value.substring( 5, value.length() - 1 ).trim();
+ return (LazyValue) t -> {
+ return lazyUIManagerGet( uiKey );
+ };
+ }
+
+ // check whether value type is specified in the value
+ if( value.startsWith( "#" ) )
+ valueType = ValueType.COLOR;
+ else if( value.startsWith( "\"" ) && value.endsWith( "\"" ) ) {
+ valueType = ValueType.STRING;
+ value = value.substring( 1, value.length() - 1 );
+ } else if( value.startsWith( TYPE_PREFIX ) ) {
+ int end = value.indexOf( TYPE_PREFIX_END );
+ if( end != -1 ) {
+ try {
+ String typeStr = value.substring( TYPE_PREFIX.length(), end );
+ valueType = ValueType.valueOf( typeStr.toUpperCase( Locale.ENGLISH ) );
+
+ // remove type from value
+ value = value.substring( end + TYPE_PREFIX_END.length() );
+ } catch( IllegalArgumentException ex ) {
+ // ignore
+ }
}
}
- }
- // determine value type from key
- if( valueType == ValueType.UNKNOWN ) {
- if( key.endsWith( "UI" ) )
- valueType = ValueType.STRING;
- else if( key.endsWith( "Color" ) ||
- (key.endsWith( "ground" ) &&
- (key.endsWith( ".background" ) || key.endsWith( "Background" ) ||
- key.endsWith( ".foreground" ) || key.endsWith( "Foreground" ))) )
- valueType = ValueType.COLOR;
- else if( key.endsWith( ".border" ) || key.endsWith( "Border" ) )
- valueType = ValueType.BORDER;
- else if( key.endsWith( ".icon" ) || key.endsWith( "Icon" ) )
- valueType = ValueType.ICON;
- else if( key.endsWith( ".margin" ) || key.endsWith( ".padding" ) ||
- key.endsWith( "Margins" ) || key.endsWith( "Insets" ) )
- valueType = ValueType.INSETS;
- else if( key.endsWith( "Size" ) )
- valueType = ValueType.DIMENSION;
- else if( key.endsWith( "Width" ) || key.endsWith( "Height" ) )
- valueType = ValueType.INTEGER;
- else if( key.endsWith( "Char" ) )
- valueType = ValueType.CHARACTER;
- else if( key.endsWith( "grayFilter" ) )
- valueType = ValueType.GRAYFILTER;
+ // determine value type from key
+ if( valueType == ValueType.UNKNOWN ) {
+ if( key.endsWith( "UI" ) )
+ valueType = ValueType.STRING;
+ else if( key.endsWith( "Color" ) ||
+ (key.endsWith( "ground" ) &&
+ (key.endsWith( ".background" ) || key.endsWith( "Background" ) || key.equals( "background" ) ||
+ key.endsWith( ".foreground" ) || key.endsWith( "Foreground" ) || key.equals( "foreground" ))) )
+ valueType = ValueType.COLOR;
+ else if( key.endsWith( ".border" ) || key.endsWith( "Border" ) || key.equals( "border" ) )
+ valueType = ValueType.BORDER;
+ else if( key.endsWith( ".icon" ) || key.endsWith( "Icon" ) || key.equals( "icon" ) )
+ valueType = ValueType.ICON;
+ else if( key.endsWith( ".margin" ) || key.equals( "margin" ) ||
+ key.endsWith( ".padding" ) || key.equals( "padding" ) ||
+ key.endsWith( "Margins" ) || key.endsWith( "Insets" ) )
+ valueType = ValueType.INSETS;
+ else if( key.endsWith( "Size" ) )
+ valueType = ValueType.DIMENSION;
+ else if( key.endsWith( "Width" ) || key.endsWith( "Height" ) )
+ valueType = ValueType.INTEGER;
+ else if( key.endsWith( "Char" ) )
+ valueType = ValueType.CHARACTER;
+ else if( key.endsWith( "grayFilter" ) )
+ valueType = ValueType.GRAYFILTER;
+ }
}
resultValueType[0] = valueType;
@@ -376,6 +434,7 @@ class UIDefaultsLoader
// parse value
switch( valueType ) {
case STRING: return value;
+ case BOOLEAN: return parseBoolean( value );
case CHARACTER: return parseCharacter( value );
case INTEGER: return parseInteger( value, true );
case FLOAT: return parseFloat( value, true );
@@ -868,6 +927,14 @@ class UIDefaultsLoader
return val;
}
+ private static Boolean parseBoolean( String value ) {
+ switch( value ) {
+ case "false": return false;
+ case "true": return true;
+ }
+ throw new IllegalArgumentException( "invalid boolean '" + value + "'" );
+ }
+
private static Character parseCharacter( String value ) {
if( value.length() != 1 )
throw new IllegalArgumentException( "invalid character '" + value + "'" );
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatAbstractIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatAbstractIcon.java
index 9675479c..8448f128 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatAbstractIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatAbstractIcon.java
@@ -39,7 +39,7 @@ public abstract class FlatAbstractIcon
{
protected final int width;
protected final int height;
- protected final Color color;
+ protected Color color;
public FlatAbstractIcon( int width, int height, Color color ) {
this.width = width;
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatAscendingSortIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatAscendingSortIcon.java
index 77ee805a..8456dc0c 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatAscendingSortIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatAscendingSortIcon.java
@@ -21,7 +21,11 @@ import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
+import javax.swing.SwingUtilities;
import javax.swing.UIManager;
+import javax.swing.plaf.TableHeaderUI;
+import javax.swing.table.JTableHeader;
+import com.formdev.flatlaf.ui.FlatTableHeaderUI;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
@@ -35,8 +39,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatAscendingSortIcon
extends FlatAbstractIcon
{
- protected final boolean chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
- protected final Color sortIconColor = UIManager.getColor( "Table.sortIconColor" );
+ protected boolean chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
+ protected Color sortIconColor = UIManager.getColor( "Table.sortIconColor" );
public FlatAscendingSortIcon() {
super( 10, 5, null );
@@ -44,7 +48,28 @@ public class FlatAscendingSortIcon
@Override
protected void paintIcon( Component c, Graphics2D g ) {
+ boolean chevron = this.chevron;
+ Color sortIconColor = this.sortIconColor;
+
+ // Because this icons are always shared for all table headers,
+ // get icon specific style from FlatTableHeaderUI.
+ JTableHeader tableHeader = (JTableHeader) SwingUtilities.getAncestorOfClass( JTableHeader.class, c );
+ if( tableHeader != null ) {
+ TableHeaderUI ui = tableHeader.getUI();
+ if( ui instanceof FlatTableHeaderUI ) {
+ FlatTableHeaderUI fui = (FlatTableHeaderUI) ui;
+ if( fui.arrowType != null )
+ chevron = FlatUIUtils.isChevron( fui.arrowType );
+ if( fui.sortIconColor != null )
+ sortIconColor = fui.sortIconColor;
+ }
+ }
+
g.setColor( sortIconColor );
+ paintArrow( c, g, chevron );
+ }
+
+ protected void paintArrow( Component c, Graphics2D g, boolean chevron ) {
if( chevron ) {
// chevron arrow
Path2D path = FlatUIUtils.createPath( false, 1,4, 5,0, 9,4 );
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatCapsLockIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatCapsLockIcon.java
index 64fedf47..5db34209 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatCapsLockIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatCapsLockIcon.java
@@ -16,12 +16,14 @@
package com.formdev.flatlaf.icons;
+import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.UIManager;
+import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
@@ -38,6 +40,17 @@ public class FlatCapsLockIcon
super( 16, 16, UIManager.getColor( "PasswordField.capsLockIconColor" ) );
}
+ /**
+ * @since 2
+ */
+ public Object applyStyleProperty( String key, Object value ) {
+ Object oldValue;
+ switch( key ) {
+ case "capsLockIconColor": oldValue = color; color = (Color) value; return oldValue;
+ default: throw new UnknownStyleException( key );
+ }
+ }
+
@Override
protected void paintIcon( Component c, Graphics2D g ) {
/*
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatCheckBoxIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatCheckBoxIcon.java
index c74c6976..7249c884 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatCheckBoxIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatCheckBoxIcon.java
@@ -23,10 +23,13 @@ import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.awt.geom.RoundRectangle2D;
+import java.util.Map;
import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
@@ -67,39 +70,39 @@ public class FlatCheckBoxIcon
extends FlatAbstractIcon
{
protected final String style = UIManager.getString( "CheckBox.icon.style" );
- public final int focusWidth = getUIInt( "CheckBox.icon.focusWidth",
+ @Styleable public int focusWidth = getUIInt( "CheckBox.icon.focusWidth",
UIManager.getInt( "Component.focusWidth" ), style );
- protected final Color focusColor = FlatUIUtils.getUIColor( "CheckBox.icon.focusColor",
+ @Styleable protected Color focusColor = FlatUIUtils.getUIColor( "CheckBox.icon.focusColor",
UIManager.getColor( "Component.focusColor" ) );
- protected final int arc = FlatUIUtils.getUIInt( "CheckBox.arc", 2 );
+ @Styleable protected int arc = FlatUIUtils.getUIInt( "CheckBox.arc", 2 );
// enabled
- protected final Color borderColor = getUIColor( "CheckBox.icon.borderColor", style );
- protected final Color background = getUIColor( "CheckBox.icon.background", style );
- protected final Color selectedBorderColor = getUIColor( "CheckBox.icon.selectedBorderColor", style );
- protected final Color selectedBackground = getUIColor( "CheckBox.icon.selectedBackground", style );
- protected final Color checkmarkColor = getUIColor( "CheckBox.icon.checkmarkColor", style );
+ @Styleable protected Color borderColor = getUIColor( "CheckBox.icon.borderColor", style );
+ @Styleable protected Color background = getUIColor( "CheckBox.icon.background", style );
+ @Styleable protected Color selectedBorderColor = getUIColor( "CheckBox.icon.selectedBorderColor", style );
+ @Styleable protected Color selectedBackground = getUIColor( "CheckBox.icon.selectedBackground", style );
+ @Styleable protected Color checkmarkColor = getUIColor( "CheckBox.icon.checkmarkColor", style );
// disabled
- protected final Color disabledBorderColor = getUIColor( "CheckBox.icon.disabledBorderColor", style );
- protected final Color disabledBackground = getUIColor( "CheckBox.icon.disabledBackground", style );
- protected final Color disabledCheckmarkColor = getUIColor( "CheckBox.icon.disabledCheckmarkColor", style );
+ @Styleable protected Color disabledBorderColor = getUIColor( "CheckBox.icon.disabledBorderColor", style );
+ @Styleable protected Color disabledBackground = getUIColor( "CheckBox.icon.disabledBackground", style );
+ @Styleable protected Color disabledCheckmarkColor = getUIColor( "CheckBox.icon.disabledCheckmarkColor", style );
// focused
- protected final Color focusedBorderColor = getUIColor( "CheckBox.icon.focusedBorderColor", style );
- protected final Color focusedBackground = getUIColor( "CheckBox.icon.focusedBackground", style );
- protected final Color selectedFocusedBorderColor = getUIColor( "CheckBox.icon.selectedFocusedBorderColor", style );
- protected final Color selectedFocusedBackground = getUIColor( "CheckBox.icon.selectedFocusedBackground", style );
- protected final Color selectedFocusedCheckmarkColor = getUIColor( "CheckBox.icon.selectedFocusedCheckmarkColor", style );
+ @Styleable protected Color focusedBorderColor = getUIColor( "CheckBox.icon.focusedBorderColor", style );
+ @Styleable protected Color focusedBackground = getUIColor( "CheckBox.icon.focusedBackground", style );
+ @Styleable protected Color selectedFocusedBorderColor = getUIColor( "CheckBox.icon.selectedFocusedBorderColor", style );
+ @Styleable protected Color selectedFocusedBackground = getUIColor( "CheckBox.icon.selectedFocusedBackground", style );
+ @Styleable protected Color selectedFocusedCheckmarkColor = getUIColor( "CheckBox.icon.selectedFocusedCheckmarkColor", style );
// hover
- protected final Color hoverBorderColor = getUIColor( "CheckBox.icon.hoverBorderColor", style );
- protected final Color hoverBackground = getUIColor( "CheckBox.icon.hoverBackground", style );
- protected final Color selectedHoverBackground = getUIColor( "CheckBox.icon.selectedHoverBackground", style );
+ @Styleable protected Color hoverBorderColor = getUIColor( "CheckBox.icon.hoverBorderColor", style );
+ @Styleable protected Color hoverBackground = getUIColor( "CheckBox.icon.hoverBackground", style );
+ @Styleable protected Color selectedHoverBackground = getUIColor( "CheckBox.icon.selectedHoverBackground", style );
// pressed
- protected final Color pressedBackground = getUIColor( "CheckBox.icon.pressedBackground", style );
- protected final Color selectedPressedBackground = getUIColor( "CheckBox.icon.selectedPressedBackground", style );
+ @Styleable protected Color pressedBackground = getUIColor( "CheckBox.icon.pressedBackground", style );
+ @Styleable protected Color selectedPressedBackground = getUIColor( "CheckBox.icon.selectedPressedBackground", style );
protected static Color getUIColor( String key, String style ) {
if( style != null ) {
@@ -129,6 +132,20 @@ public class FlatCheckBoxIcon
super( ICON_SIZE, ICON_SIZE, null );
}
+ /**
+ * @since 2
+ */
+ public Object applyStyleProperty( String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ public Map> getStyleableInfos() {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
+
@Override
protected void paintIcon( Component c, Graphics2D g ) {
boolean indeterminate = isIndeterminate( c );
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatCheckBoxMenuItemIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatCheckBoxMenuItemIcon.java
index 501602b6..7e6e0279 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatCheckBoxMenuItemIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatCheckBoxMenuItemIcon.java
@@ -21,9 +21,12 @@ import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
+import java.util.Map;
import javax.swing.AbstractButton;
import javax.swing.JMenuItem;
import javax.swing.UIManager;
+import com.formdev.flatlaf.ui.FlatStylingSupport;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
/**
* Icon for {@link javax.swing.JCheckBoxMenuItem}.
@@ -38,14 +41,28 @@ import javax.swing.UIManager;
public class FlatCheckBoxMenuItemIcon
extends FlatAbstractIcon
{
- protected final Color checkmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.checkmarkColor" );
- protected final Color disabledCheckmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.disabledCheckmarkColor" );
- protected final Color selectionForeground = UIManager.getColor( "MenuItem.selectionForeground" );
+ @Styleable protected Color checkmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.checkmarkColor" );
+ @Styleable protected Color disabledCheckmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.disabledCheckmarkColor" );
+ @Styleable protected Color selectionForeground = UIManager.getColor( "MenuItem.selectionForeground" );
public FlatCheckBoxMenuItemIcon() {
super( 15, 15, null );
}
+ /**
+ * @since 2
+ */
+ public Object applyStyleProperty( String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ public Map> getStyleableInfos() {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
+
@Override
protected void paintIcon( Component c, Graphics2D g2 ) {
boolean selected = (c instanceof AbstractButton) && ((AbstractButton)c).isSelected();
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatClearIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatClearIcon.java
index b756b509..f0e7590c 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatClearIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatClearIcon.java
@@ -22,9 +22,12 @@ import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
+import java.util.Map;
import javax.swing.AbstractButton;
import javax.swing.ButtonModel;
import javax.swing.UIManager;
+import com.formdev.flatlaf.ui.FlatStylingSupport;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
@@ -40,14 +43,28 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatClearIcon
extends FlatAbstractIcon
{
- protected Color clearIconColor = UIManager.getColor( "SearchField.clearIconColor" );
- protected Color clearIconHoverColor = UIManager.getColor( "SearchField.clearIconHoverColor" );
- protected Color clearIconPressedColor = UIManager.getColor( "SearchField.clearIconPressedColor" );
+ @Styleable protected Color clearIconColor = UIManager.getColor( "SearchField.clearIconColor" );
+ @Styleable protected Color clearIconHoverColor = UIManager.getColor( "SearchField.clearIconHoverColor" );
+ @Styleable protected Color clearIconPressedColor = UIManager.getColor( "SearchField.clearIconPressedColor" );
public FlatClearIcon() {
super( 16, 16, null );
}
+ /**
+ * @since 2
+ */
+ public Object applyStyleProperty( String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ public Map> getStyleableInfos() {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
+
@Override
protected void paintIcon( Component c, Graphics2D g ) {
if( c instanceof AbstractButton ) {
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatDescendingSortIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatDescendingSortIcon.java
index c43f751d..0ae9ac4c 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatDescendingSortIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatDescendingSortIcon.java
@@ -17,11 +17,9 @@
package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
-import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
-import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
@@ -33,18 +31,14 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
* @author Karl Tauber
*/
public class FlatDescendingSortIcon
- extends FlatAbstractIcon
+ extends FlatAscendingSortIcon
{
- protected final boolean chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
- protected final Color sortIconColor = UIManager.getColor( "Table.sortIconColor" );
-
public FlatDescendingSortIcon() {
- super( 10, 5, null );
+ super();
}
@Override
- protected void paintIcon( Component c, Graphics2D g ) {
- g.setColor( sortIconColor );
+ protected void paintArrow( Component c, Graphics2D g, boolean chevron ) {
if( chevron ) {
// chevron arrow
Path2D path = FlatUIUtils.createPath( false, 1,0, 5,4, 9,0 );
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatHelpButtonIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatHelpButtonIcon.java
index 9283b49d..f4f62d1c 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatHelpButtonIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatHelpButtonIcon.java
@@ -22,8 +22,11 @@ import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
+import java.util.Map;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
@@ -50,29 +53,41 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatHelpButtonIcon
extends FlatAbstractIcon
{
- protected final int focusWidth = UIManager.getInt( "Component.focusWidth" );
- protected final Color focusColor = UIManager.getColor( "Component.focusColor" );
- protected final float innerFocusWidth = FlatUIUtils.getUIFloat( "HelpButton.innerFocusWidth", FlatUIUtils.getUIFloat( "Component.innerFocusWidth", 0 ) );
- protected final int borderWidth = FlatUIUtils.getUIInt( "HelpButton.borderWidth", 1 );
+ @Styleable protected int focusWidth = UIManager.getInt( "Component.focusWidth" );
+ @Styleable protected Color focusColor = UIManager.getColor( "Component.focusColor" );
+ @Styleable protected float innerFocusWidth = FlatUIUtils.getUIFloat( "HelpButton.innerFocusWidth", FlatUIUtils.getUIFloat( "Component.innerFocusWidth", 0 ) );
+ @Styleable protected int borderWidth = FlatUIUtils.getUIInt( "HelpButton.borderWidth", 1 );
- protected final Color borderColor = UIManager.getColor( "HelpButton.borderColor" );
- protected final Color disabledBorderColor = UIManager.getColor( "HelpButton.disabledBorderColor" );
- protected final Color focusedBorderColor = UIManager.getColor( "HelpButton.focusedBorderColor" );
- protected final Color hoverBorderColor = UIManager.getColor( "HelpButton.hoverBorderColor" );
- protected final Color background = UIManager.getColor( "HelpButton.background" );
- protected final Color disabledBackground = UIManager.getColor( "HelpButton.disabledBackground" );
- protected final Color focusedBackground = UIManager.getColor( "HelpButton.focusedBackground" );
- protected final Color hoverBackground = UIManager.getColor( "HelpButton.hoverBackground" );
- protected final Color pressedBackground = UIManager.getColor( "HelpButton.pressedBackground" );
- protected final Color questionMarkColor = UIManager.getColor( "HelpButton.questionMarkColor" );
- protected final Color disabledQuestionMarkColor = UIManager.getColor( "HelpButton.disabledQuestionMarkColor" );
-
- protected final int iconSize = 22 + (focusWidth * 2);
+ @Styleable protected Color borderColor = UIManager.getColor( "HelpButton.borderColor" );
+ @Styleable protected Color disabledBorderColor = UIManager.getColor( "HelpButton.disabledBorderColor" );
+ @Styleable protected Color focusedBorderColor = UIManager.getColor( "HelpButton.focusedBorderColor" );
+ @Styleable protected Color hoverBorderColor = UIManager.getColor( "HelpButton.hoverBorderColor" );
+ @Styleable protected Color background = UIManager.getColor( "HelpButton.background" );
+ @Styleable protected Color disabledBackground = UIManager.getColor( "HelpButton.disabledBackground" );
+ @Styleable protected Color focusedBackground = UIManager.getColor( "HelpButton.focusedBackground" );
+ @Styleable protected Color hoverBackground = UIManager.getColor( "HelpButton.hoverBackground" );
+ @Styleable protected Color pressedBackground = UIManager.getColor( "HelpButton.pressedBackground" );
+ @Styleable protected Color questionMarkColor = UIManager.getColor( "HelpButton.questionMarkColor" );
+ @Styleable protected Color disabledQuestionMarkColor = UIManager.getColor( "HelpButton.disabledQuestionMarkColor" );
public FlatHelpButtonIcon() {
super( 0, 0, null );
}
+ /**
+ * @since 2
+ */
+ public Object applyStyleProperty( String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ public Map> getStyleableInfos() {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
+
@Override
protected void paintIcon( Component c, Graphics2D g2 ) {
/*
@@ -89,7 +104,7 @@ public class FlatHelpButtonIcon
boolean focused = FlatUIUtils.isPermanentFocusOwner( c );
float xy = 0.5f;
- float wh = iconSize - 1;
+ float wh = iconSize() - 1;
// paint outer focus border
if( focused && FlatButtonUI.isFocusPainted( c ) ) {
@@ -151,11 +166,15 @@ public class FlatHelpButtonIcon
@Override
public int getIconWidth() {
- return scale( iconSize );
+ return scale( iconSize() );
}
@Override
public int getIconHeight() {
- return scale( iconSize );
+ return scale( iconSize() );
+ }
+
+ private int iconSize() {
+ return 22 + (focusWidth * 2);
}
}
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatMenuArrowIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatMenuArrowIcon.java
index 2afb8faf..b346820e 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatMenuArrowIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatMenuArrowIcon.java
@@ -21,9 +21,12 @@ import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
+import java.util.Map;
import javax.swing.JMenu;
import javax.swing.UIManager;
+import com.formdev.flatlaf.ui.FlatStylingSupport;
import com.formdev.flatlaf.ui.FlatUIUtils;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
/**
* "arrow" icon for {@link javax.swing.JMenu}.
@@ -39,22 +42,36 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatMenuArrowIcon
extends FlatAbstractIcon
{
- protected final boolean chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
- protected final Color arrowColor = UIManager.getColor( "Menu.icon.arrowColor" );
- protected final Color disabledArrowColor = UIManager.getColor( "Menu.icon.disabledArrowColor" );
- protected final Color selectionForeground = UIManager.getColor( "Menu.selectionForeground" );
+ @Styleable protected String arrowType = UIManager.getString( "Component.arrowType" );
+ @Styleable protected Color arrowColor = UIManager.getColor( "Menu.icon.arrowColor" );
+ @Styleable protected Color disabledArrowColor = UIManager.getColor( "Menu.icon.disabledArrowColor" );
+ @Styleable protected Color selectionForeground = UIManager.getColor( "Menu.selectionForeground" );
public FlatMenuArrowIcon() {
super( 6, 10, null );
}
+ /**
+ * @since 2
+ */
+ public Object applyStyleProperty( String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ public Map> getStyleableInfos() {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
+
@Override
protected void paintIcon( Component c, Graphics2D g ) {
if( !c.getComponentOrientation().isLeftToRight() )
g.rotate( Math.toRadians( 180 ), width / 2., height / 2. );
g.setColor( getArrowColor( c ) );
- if( chevron ) {
+ if( FlatUIUtils.isChevron( arrowType ) ) {
// chevron arrow
Path2D path = FlatUIUtils.createPath( false, 1,1, 5,5, 1,9 );
g.setStroke( new BasicStroke( 1f ) );
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatRadioButtonIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatRadioButtonIcon.java
index 06095b0f..381d2910 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatRadioButtonIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatRadioButtonIcon.java
@@ -19,6 +19,7 @@ package com.formdev.flatlaf.icons;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
/**
* Icon for {@link javax.swing.JRadioButton}.
@@ -34,7 +35,7 @@ import java.awt.geom.Ellipse2D;
public class FlatRadioButtonIcon
extends FlatCheckBoxIcon
{
- protected final int centerDiameter = getUIInt( "RadioButton.icon.centerDiameter", 8, style );
+ @Styleable protected int centerDiameter = getUIInt( "RadioButton.icon.centerDiameter", 8, style );
@Override
protected void paintFocusBorder( Component c, Graphics2D g ) {
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatSearchIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatSearchIcon.java
index 1bc726a6..0549b813 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatSearchIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatSearchIcon.java
@@ -21,8 +21,11 @@ import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
+import java.util.Map;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
@@ -38,14 +41,28 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatSearchIcon
extends FlatAbstractIcon
{
- protected Color searchIconColor = UIManager.getColor( "SearchField.searchIconColor" );
- protected Color searchIconHoverColor = UIManager.getColor( "SearchField.searchIconHoverColor" );
- protected Color searchIconPressedColor = UIManager.getColor( "SearchField.searchIconPressedColor" );
+ @Styleable protected Color searchIconColor = UIManager.getColor( "SearchField.searchIconColor" );
+ @Styleable protected Color searchIconHoverColor = UIManager.getColor( "SearchField.searchIconHoverColor" );
+ @Styleable protected Color searchIconPressedColor = UIManager.getColor( "SearchField.searchIconPressedColor" );
public FlatSearchIcon() {
super( 16, 16, null );
}
+ /**
+ * @since 2
+ */
+ public Object applyStyleProperty( String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ public Map> getStyleableInfos() {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
+
@Override
protected void paintIcon( Component c, Graphics2D g ) {
/*
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTabbedPaneCloseIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTabbedPaneCloseIcon.java
index 39e90587..f93d71d3 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTabbedPaneCloseIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTabbedPaneCloseIcon.java
@@ -23,8 +23,11 @@ import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
+import java.util.Map;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
@@ -47,39 +50,53 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatTabbedPaneCloseIcon
extends FlatAbstractIcon
{
- protected final Dimension size = UIManager.getDimension( "TabbedPane.closeSize" );
- protected final int arc = UIManager.getInt( "TabbedPane.closeArc" );
- protected final float crossPlainSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossPlainSize", 7.5f );
- protected final float crossFilledSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossFilledSize", crossPlainSize );
- protected final float closeCrossLineWidth = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossLineWidth", 1f );
- protected final Color background = UIManager.getColor( "TabbedPane.closeBackground" );
- protected final Color foreground = UIManager.getColor( "TabbedPane.closeForeground" );
- protected final Color hoverBackground = UIManager.getColor( "TabbedPane.closeHoverBackground" );
- protected final Color hoverForeground = UIManager.getColor( "TabbedPane.closeHoverForeground" );
- protected final Color pressedBackground = UIManager.getColor( "TabbedPane.closePressedBackground" );
- protected final Color pressedForeground = UIManager.getColor( "TabbedPane.closePressedForeground" );
+ @Styleable protected Dimension closeSize = UIManager.getDimension( "TabbedPane.closeSize" );
+ @Styleable protected int closeArc = UIManager.getInt( "TabbedPane.closeArc" );
+ @Styleable protected float closeCrossPlainSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossPlainSize", 7.5f );
+ @Styleable protected float closeCrossFilledSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossFilledSize", closeCrossPlainSize );
+ @Styleable protected float closeCrossLineWidth = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossLineWidth", 1f );
+ @Styleable protected Color closeBackground = UIManager.getColor( "TabbedPane.closeBackground" );
+ @Styleable protected Color closeForeground = UIManager.getColor( "TabbedPane.closeForeground" );
+ @Styleable protected Color closeHoverBackground = UIManager.getColor( "TabbedPane.closeHoverBackground" );
+ @Styleable protected Color closeHoverForeground = UIManager.getColor( "TabbedPane.closeHoverForeground" );
+ @Styleable protected Color closePressedBackground = UIManager.getColor( "TabbedPane.closePressedBackground" );
+ @Styleable protected Color closePressedForeground = UIManager.getColor( "TabbedPane.closePressedForeground" );
public FlatTabbedPaneCloseIcon() {
super( 16, 16, null );
}
+ /**
+ * @since 2
+ */
+ public Object applyStyleProperty( String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ public Map> getStyleableInfos() {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
+
@Override
protected void paintIcon( Component c, Graphics2D g ) {
// paint background
- Color bg = FlatButtonUI.buttonStateColor( c, background, null, null, hoverBackground, pressedBackground );
+ Color bg = FlatButtonUI.buttonStateColor( c, closeBackground, null, null, closeHoverBackground, closePressedBackground );
if( bg != null ) {
g.setColor( FlatUIUtils.deriveColor( bg, c.getBackground() ) );
- g.fillRoundRect( (width - size.width) / 2, (height - size.height) / 2,
- size.width, size.height, arc, arc );
+ g.fillRoundRect( (width - closeSize.width) / 2, (height - closeSize.height) / 2,
+ closeSize.width, closeSize.height, closeArc, closeArc );
}
// set cross color
- Color fg = FlatButtonUI.buttonStateColor( c, foreground, null, null, hoverForeground, pressedForeground );
+ Color fg = FlatButtonUI.buttonStateColor( c, closeForeground, null, null, closeHoverForeground, closePressedForeground );
g.setColor( FlatUIUtils.deriveColor( fg, c.getForeground() ) );
float mx = width / 2;
float my = height / 2;
- float r = ((bg != null) ? crossFilledSize : crossPlainSize) / 2;
+ float r = ((bg != null) ? closeCrossFilledSize : closeCrossPlainSize) / 2;
// paint cross
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTreeClosedIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTreeClosedIcon.java
index df160011..c149bb9c 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTreeClosedIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTreeClosedIcon.java
@@ -37,6 +37,8 @@ public class FlatTreeClosedIcon
@Override
protected void paintIcon( Component c, Graphics2D g ) {
+ FlatTreeCollapsedIcon.setStyleColorFromTreeUI( c, g, ui -> ui.iconClosedColor );
+
/*
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTreeCollapsedIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTreeCollapsedIcon.java
index 481b2e42..d245f79c 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTreeCollapsedIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTreeCollapsedIcon.java
@@ -19,7 +19,12 @@ package com.formdev.flatlaf.icons;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
+import java.util.function.Function;
+import javax.swing.JTree;
+import javax.swing.SwingUtilities;
import javax.swing.UIManager;
+import javax.swing.plaf.TreeUI;
+import com.formdev.flatlaf.ui.FlatTreeUI;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
@@ -46,8 +51,12 @@ public class FlatTreeCollapsedIcon
@Override
protected void paintIcon( Component c, Graphics2D g ) {
+ setStyleColorFromTreeUI( c, g );
rotate( c, g );
+ String arrowType = getStyleFromTreeUI( c, ui -> ui.iconArrowType );
+ boolean chevron = (arrowType != null) ? FlatUIUtils.isChevron( arrowType ) : this.chevron;
+
if( chevron ) {
// chevron arrow
g.fill( FlatUIUtils.createPath( 3,1, 3,2.5, 6,5.5, 3,8.5, 3,10, 4.5,10, 9,5.5, 4.5,1 ) );
@@ -57,8 +66,34 @@ public class FlatTreeCollapsedIcon
}
}
+ void setStyleColorFromTreeUI( Component c, Graphics2D g ) {
+ setStyleColorFromTreeUI( c, g, ui -> ui.iconCollapsedColor );
+ }
+
void rotate( Component c, Graphics2D g ) {
if( !c.getComponentOrientation().isLeftToRight() )
g.rotate( Math.toRadians( 180 ), width / 2., height / 2. );
}
+
+ /**
+ * Because this icons are always shared for all trees,
+ * get icon specific style from FlatTreeUI.
+ */
+ static T getStyleFromTreeUI( Component c, Function f ) {
+ JTree tree = (c instanceof JTree)
+ ? (JTree) c
+ : (JTree) SwingUtilities.getAncestorOfClass( JTree.class, c );
+ if( tree != null ) {
+ TreeUI ui = tree.getUI();
+ if( ui instanceof FlatTreeUI )
+ return f.apply( (FlatTreeUI) ui );
+ }
+ return null;
+ }
+
+ static void setStyleColorFromTreeUI( Component c, Graphics2D g, Function f ) {
+ Color color = getStyleFromTreeUI( c, f );
+ if( color != null )
+ g.setColor( color );
+ }
}
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTreeExpandedIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTreeExpandedIcon.java
index 113139e9..3e99f3ef 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTreeExpandedIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTreeExpandedIcon.java
@@ -34,6 +34,11 @@ public class FlatTreeExpandedIcon
super( UIManager.getColor( "Tree.icon.expandedColor" ) );
}
+ @Override
+ void setStyleColorFromTreeUI( Component c, Graphics2D g ) {
+ setStyleColorFromTreeUI( c, g, ui -> ui.iconExpandedColor );
+ }
+
@Override
void rotate( Component c, Graphics2D g ) {
g.rotate( Math.toRadians( 90 ), width / 2., height / 2. );
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTreeLeafIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTreeLeafIcon.java
index 6ec8900d..b85ccbb1 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTreeLeafIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTreeLeafIcon.java
@@ -37,6 +37,8 @@ public class FlatTreeLeafIcon
@Override
protected void paintIcon( Component c, Graphics2D g ) {
+ FlatTreeCollapsedIcon.setStyleColorFromTreeUI( c, g, ui -> ui.iconLeafColor );
+
/*
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTreeOpenIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTreeOpenIcon.java
index f8754d9e..e2ca5213 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTreeOpenIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTreeOpenIcon.java
@@ -37,6 +37,8 @@ public class FlatTreeOpenIcon
@Override
protected void paintIcon( Component c, Graphics2D g ) {
+ FlatTreeCollapsedIcon.setStyleColorFromTreeUI( c, g, ui -> ui.iconOpenColor );
+
/*
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatArrowButton.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatArrowButton.java
index 53088a60..6fc774fd 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatArrowButton.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatArrowButton.java
@@ -39,13 +39,13 @@ public class FlatArrowButton
{
public static final int DEFAULT_ARROW_WIDTH = 8;
- protected final boolean chevron;
- protected final Color foreground;
- protected final Color disabledForeground;
- protected final Color hoverForeground;
- protected final Color hoverBackground;
- protected final Color pressedForeground;
- protected final Color pressedBackground;
+ protected boolean chevron;
+ protected Color foreground;
+ protected Color disabledForeground;
+ protected Color hoverForeground;
+ protected Color hoverBackground;
+ protected Color pressedForeground;
+ protected Color pressedBackground;
private int arrowWidth = DEFAULT_ARROW_WIDTH;
private float xOffset = 0;
@@ -58,14 +58,8 @@ public class FlatArrowButton
Color hoverForeground, Color hoverBackground, Color pressedForeground, Color pressedBackground )
{
super( direction, Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE );
-
- this.chevron = FlatUIUtils.isChevron( type );
- this.foreground = foreground;
- this.disabledForeground = disabledForeground;
- this.hoverForeground = hoverForeground;
- this.hoverBackground = hoverBackground;
- this.pressedForeground = pressedForeground;
- this.pressedBackground = pressedBackground;
+ updateStyle( type, foreground, disabledForeground, hoverForeground, hoverBackground,
+ pressedForeground, pressedBackground );
setOpaque( false );
setBorder( null );
@@ -101,6 +95,21 @@ public class FlatArrowButton
}
}
+ /**
+ * @since 2
+ */
+ public void updateStyle( String type, Color foreground, Color disabledForeground,
+ Color hoverForeground, Color hoverBackground, Color pressedForeground, Color pressedBackground )
+ {
+ this.chevron = FlatUIUtils.isChevron( type );
+ this.foreground = foreground;
+ this.disabledForeground = disabledForeground;
+ this.hoverForeground = hoverForeground;
+ this.hoverBackground = hoverBackground;
+ this.pressedForeground = pressedForeground;
+ this.pressedBackground = pressedBackground;
+ }
+
public int getArrowWidth() {
return arrowWidth;
}
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatBorder.java
index 276a591a..dccdffb5 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatBorder.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatBorder.java
@@ -23,6 +23,7 @@ import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Paint;
+import java.util.Map;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
@@ -31,6 +32,8 @@ import javax.swing.JViewport;
import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicBorders;
import com.formdev.flatlaf.FlatClientProperties;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
import com.formdev.flatlaf.util.DerivedColor;
/**
@@ -61,20 +64,37 @@ import com.formdev.flatlaf.util.DerivedColor;
*/
public class FlatBorder
extends BasicBorders.MarginBorder
+ implements StyleableBorder
{
- protected final int focusWidth = UIManager.getInt( "Component.focusWidth" );
- protected final float innerFocusWidth = FlatUIUtils.getUIFloat( "Component.innerFocusWidth", 0 );
- protected final float innerOutlineWidth = FlatUIUtils.getUIFloat( "Component.innerOutlineWidth", 0 );
- protected final Color focusColor = UIManager.getColor( "Component.focusColor" );
- protected final Color borderColor = UIManager.getColor( "Component.borderColor" );
- protected final Color disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
- protected final Color focusedBorderColor = UIManager.getColor( "Component.focusedBorderColor" );
+ @Styleable protected int focusWidth = UIManager.getInt( "Component.focusWidth" );
+ @Styleable protected float innerFocusWidth = FlatUIUtils.getUIFloat( "Component.innerFocusWidth", 0 );
+ @Styleable protected float innerOutlineWidth = FlatUIUtils.getUIFloat( "Component.innerOutlineWidth", 0 );
+ @Styleable protected Color focusColor = UIManager.getColor( "Component.focusColor" );
+ @Styleable protected Color borderColor = UIManager.getColor( "Component.borderColor" );
+ @Styleable protected Color disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
+ @Styleable protected Color focusedBorderColor = UIManager.getColor( "Component.focusedBorderColor" );
- protected final Color errorBorderColor = UIManager.getColor( "Component.error.borderColor" );
- protected final Color errorFocusedBorderColor = UIManager.getColor( "Component.error.focusedBorderColor" );
- protected final Color warningBorderColor = UIManager.getColor( "Component.warning.borderColor" );
- protected final Color warningFocusedBorderColor = UIManager.getColor( "Component.warning.focusedBorderColor" );
- protected final Color customBorderColor = UIManager.getColor( "Component.custom.borderColor" );
+ @Styleable(dot=true) protected Color errorBorderColor = UIManager.getColor( "Component.error.borderColor" );
+ @Styleable(dot=true) protected Color errorFocusedBorderColor = UIManager.getColor( "Component.error.focusedBorderColor" );
+ @Styleable(dot=true) protected Color warningBorderColor = UIManager.getColor( "Component.warning.borderColor" );
+ @Styleable(dot=true) protected Color warningFocusedBorderColor = UIManager.getColor( "Component.warning.focusedBorderColor" );
+ @Styleable(dot=true) protected Color customBorderColor = UIManager.getColor( "Component.custom.borderColor" );
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Object applyStyleProperty( String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos() {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonBorder.java
index d813c4b6..d9ac552a 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonBorder.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonBorder.java
@@ -26,6 +26,7 @@ import java.awt.Paint;
import javax.swing.AbstractButton;
import javax.swing.UIManager;
import javax.swing.plaf.UIResource;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -40,16 +41,16 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Button.default.borderColor Color
* @uiDefault Button.default.startBorderColor Color optional; if set, a gradient paint is used and Button.default.borderColor is ignored
* @uiDefault Button.default.endBorderColor Color optional; if set, a gradient paint is used
- * @uiDefault Button.default.hoverBorderColor Color optional
* @uiDefault Button.default.focusedBorderColor Color
* @uiDefault Button.default.focusColor Color
+ * @uiDefault Button.default.hoverBorderColor Color optional
* @uiDefault Button.toolbar.focusColor Color optional; defaults to Component.focusColor
* @uiDefault Button.borderWidth int
* @uiDefault Button.default.borderWidth int
* @uiDefault Button.innerFocusWidth int or float optional; defaults to Component.innerFocusWidth
* @uiDefault Button.toolbar.margin Insets
* @uiDefault Button.toolbar.spacingInsets Insets
- * @uiDefault Button.toolbar.focusWidth int or float optional; default is 1
+ * @uiDefault Button.toolbar.focusWidth int or float optional; default is 1.5
* @uiDefault Button.arc int
*
* @author Karl Tauber
@@ -57,26 +58,31 @@ import com.formdev.flatlaf.util.UIScale;
public class FlatButtonBorder
extends FlatBorder
{
- protected final Color borderColor = FlatUIUtils.getUIColor( "Button.startBorderColor", "Button.borderColor" );
- protected final Color endBorderColor = UIManager.getColor( "Button.endBorderColor" );
- protected final Color disabledBorderColor = UIManager.getColor( "Button.disabledBorderColor" );
- protected final Color focusedBorderColor = UIManager.getColor( "Button.focusedBorderColor" );
- protected final Color hoverBorderColor = UIManager.getColor( "Button.hoverBorderColor" );
- protected final Color defaultBorderColor = FlatUIUtils.getUIColor( "Button.default.startBorderColor", "Button.default.borderColor" );
- protected final Color defaultEndBorderColor = UIManager.getColor( "Button.default.endBorderColor" );
- protected final Color defaultHoverBorderColor = UIManager.getColor( "Button.default.hoverBorderColor" );
- protected final Color defaultFocusedBorderColor = UIManager.getColor( "Button.default.focusedBorderColor" );
- protected final Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
+ @Styleable protected Color borderColor = FlatUIUtils.getUIColor( "Button.startBorderColor", "Button.borderColor" );
+ protected Color endBorderColor = UIManager.getColor( "Button.endBorderColor" );
+ @Styleable protected Color disabledBorderColor = UIManager.getColor( "Button.disabledBorderColor" );
+ @Styleable protected Color focusedBorderColor = UIManager.getColor( "Button.focusedBorderColor" );
+ @Styleable protected Color hoverBorderColor = UIManager.getColor( "Button.hoverBorderColor" );
+
+ @Styleable(dot=true) protected Color defaultBorderColor = FlatUIUtils.getUIColor( "Button.default.startBorderColor", "Button.default.borderColor" );
+ protected Color defaultEndBorderColor = UIManager.getColor( "Button.default.endBorderColor" );
+ @Styleable(dot=true) protected Color defaultFocusedBorderColor = UIManager.getColor( "Button.default.focusedBorderColor" );
+ @Styleable(dot=true) protected Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
+ @Styleable(dot=true) protected Color defaultHoverBorderColor = UIManager.getColor( "Button.default.hoverBorderColor" );
/** @since 1.4 */
- protected final Color toolbarFocusColor = UIManager.getColor( "Button.toolbar.focusColor" );
- protected final int borderWidth = UIManager.getInt( "Button.borderWidth" );
- protected final int defaultBorderWidth = UIManager.getInt( "Button.default.borderWidth" );
- protected final float buttonInnerFocusWidth = FlatUIUtils.getUIFloat( "Button.innerFocusWidth", innerFocusWidth );
- protected final Insets toolbarMargin = UIManager.getInsets( "Button.toolbar.margin" );
- protected final Insets toolbarSpacingInsets = UIManager.getInsets( "Button.toolbar.spacingInsets" );
+ @Styleable(dot=true) protected Color toolbarFocusColor = UIManager.getColor( "Button.toolbar.focusColor" );
+
+ @Styleable protected int borderWidth = UIManager.getInt( "Button.borderWidth" );
+ @Styleable(dot=true) protected int defaultBorderWidth = UIManager.getInt( "Button.default.borderWidth" );
+ @Styleable(dot=true) protected Insets toolbarMargin = UIManager.getInsets( "Button.toolbar.margin" );
+ @Styleable(dot=true) protected Insets toolbarSpacingInsets = UIManager.getInsets( "Button.toolbar.spacingInsets" );
/** @since 1.4 */
- protected final float toolbarFocusWidth = FlatUIUtils.getUIFloat( "Button.toolbar.focusWidth", 1.5f );
- protected final int arc = UIManager.getInt( "Button.arc" );
+ @Styleable(dot=true) protected float toolbarFocusWidth = FlatUIUtils.getUIFloat( "Button.toolbar.focusWidth", 1.5f );
+ @Styleable protected int arc = UIManager.getInt( "Button.arc" );
+
+ public FlatButtonBorder() {
+ innerFocusWidth = FlatUIUtils.getUIFloat( "Button.innerFocusWidth", innerFocusWidth );
+ }
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
@@ -174,11 +180,6 @@ public class FlatButtonBorder
return FlatToggleButtonUI.isTabButton( c ) ? 0 : super.getFocusWidth( c );
}
- @Override
- protected float getInnerFocusWidth( Component c ) {
- return buttonInnerFocusWidth;
- }
-
@Override
protected int getBorderWidth( Component c ) {
return FlatButtonUI.isDefaultButton( c ) ? defaultBorderWidth : borderWidth;
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonUI.java
index 0afd6eb1..a171cfe2 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonUI.java
@@ -30,7 +30,9 @@ import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.geom.RoundRectangle2D;
import java.beans.PropertyChangeEvent;
+import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.AbstractButton;
import javax.swing.ButtonModel;
import javax.swing.Icon;
@@ -45,6 +47,10 @@ import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicButtonListener;
import javax.swing.plaf.basic.BasicButtonUI;
import com.formdev.flatlaf.FlatLaf;
+import com.formdev.flatlaf.icons.FlatHelpButtonIcon;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -94,8 +100,9 @@ import com.formdev.flatlaf.util.UIScale;
*/
public class FlatButtonUI
extends BasicButtonUI
+ implements StyleableUI
{
- protected int minimumWidth;
+ @Styleable protected int minimumWidth;
protected int iconTextGap;
protected Color background;
@@ -103,39 +110,57 @@ public class FlatButtonUI
protected Color startBackground;
protected Color endBackground;
- protected Color focusedBackground;
- protected Color hoverBackground;
- protected Color pressedBackground;
- protected Color selectedBackground;
- protected Color selectedForeground;
- protected Color disabledBackground;
- protected Color disabledText;
- protected Color disabledSelectedBackground;
+ @Styleable protected Color focusedBackground;
+ @Styleable protected Color hoverBackground;
+ @Styleable protected Color pressedBackground;
+ @Styleable protected Color selectedBackground;
+ @Styleable protected Color selectedForeground;
+ @Styleable protected Color disabledBackground;
+ @Styleable protected Color disabledText;
+ @Styleable protected Color disabledSelectedBackground;
- protected Color defaultBackground;
+ @Styleable(dot=true) protected Color defaultBackground;
protected Color defaultEndBackground;
- protected Color defaultForeground;
- protected Color defaultFocusedBackground;
- protected Color defaultHoverBackground;
- protected Color defaultPressedBackground;
- protected boolean defaultBoldText;
+ @Styleable(dot=true) protected Color defaultForeground;
+ @Styleable(dot=true) protected Color defaultFocusedBackground;
+ @Styleable(dot=true) protected Color defaultHoverBackground;
+ @Styleable(dot=true) protected Color defaultPressedBackground;
+ @Styleable(dot=true) protected boolean defaultBoldText;
- protected int shadowWidth;
- protected Color shadowColor;
- protected Color defaultShadowColor;
+ @Styleable protected boolean paintShadow;
+ @Styleable protected int shadowWidth;
+ @Styleable protected Color shadowColor;
+ @Styleable(dot=true) protected Color defaultShadowColor;
- protected Insets toolbarSpacingInsets;
- protected Color toolbarHoverBackground;
- protected Color toolbarPressedBackground;
- protected Color toolbarSelectedBackground;
+ @Styleable(dot=true) protected Insets toolbarSpacingInsets;
+ @Styleable(dot=true) protected Color toolbarHoverBackground;
+ @Styleable(dot=true) protected Color toolbarPressedBackground;
+ @Styleable(dot=true) protected Color toolbarSelectedBackground;
private Icon helpButtonIcon;
private Insets defaultMargin;
+ private final boolean shared;
+ private boolean helpButtonIconShared = true;
private boolean defaults_initialized = false;
+ private Map oldStyleValues;
+ private AtomicBoolean borderShared;
public static ComponentUI createUI( JComponent c ) {
- return FlatUIUtils.createSharedUI( FlatButtonUI.class, FlatButtonUI::new );
+ return FlatUIUtils.canUseSharedUI( c )
+ ? FlatUIUtils.createSharedUI( FlatButtonUI.class, () -> new FlatButtonUI( true ) )
+ : new FlatButtonUI( false );
+ }
+
+ protected FlatButtonUI( boolean shared ) {
+ this.shared = shared;
+ }
+
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( (AbstractButton) c, FlatStylingSupport.getStyle( c ) );
}
@Override
@@ -162,16 +187,6 @@ public class FlatButtonUI
disabledText = UIManager.getColor( prefix + "disabledText" );
disabledSelectedBackground = UIManager.getColor( prefix + "disabledSelectedBackground" );
- if( UIManager.getBoolean( "Button.paintShadow" ) ) {
- shadowWidth = FlatUIUtils.getUIInt( "Button.shadowWidth", 2 );
- shadowColor = UIManager.getColor( "Button.shadowColor" );
- defaultShadowColor = UIManager.getColor( "Button.default.shadowColor" );
- } else {
- shadowWidth = 0;
- shadowColor = null;
- defaultShadowColor = null;
- }
-
defaultBackground = FlatUIUtils.getUIColor( "Button.default.startBackground", "Button.default.background" );
defaultEndBackground = UIManager.getColor( "Button.default.endBackground" );
defaultForeground = UIManager.getColor( "Button.default.foreground" );
@@ -180,6 +195,11 @@ public class FlatButtonUI
defaultPressedBackground = UIManager.getColor( "Button.default.pressedBackground" );
defaultBoldText = UIManager.getBoolean( "Button.default.boldText" );
+ paintShadow = UIManager.getBoolean( "Button.paintShadow" );
+ shadowWidth = FlatUIUtils.getUIInt( "Button.shadowWidth", 2 );
+ shadowColor = UIManager.getColor( "Button.shadowColor" );
+ defaultShadowColor = UIManager.getColor( "Button.default.shadowColor" );
+
toolbarSpacingInsets = UIManager.getInsets( "Button.toolbar.spacingInsets" );
toolbarHoverBackground = UIManager.getColor( prefix + "toolbar.hoverBackground" );
toolbarPressedBackground = UIManager.getColor( prefix + "toolbar.pressedBackground" );
@@ -188,6 +208,7 @@ public class FlatButtonUI
helpButtonIcon = UIManager.getIcon( "HelpButton.icon" );
defaultMargin = UIManager.getInsets( prefix + "margin" );
+ helpButtonIconShared = true;
defaults_initialized = true;
}
@@ -207,6 +228,9 @@ public class FlatButtonUI
protected void uninstallDefaults( AbstractButton b ) {
super.uninstallDefaults( b );
+ oldStyleValues = null;
+ borderShared = null;
+
MigLayoutVisualPadding.uninstall( b );
defaults_initialized = false;
}
@@ -228,9 +252,62 @@ public class FlatButtonUI
b.revalidate();
b.repaint();
break;
+
+ case STYLE:
+ Object style = e.getNewValue();
+ if( style != null && shared ) {
+ // unshare component UI if necessary
+ // updateUI() invokes applyStyle() from installUI()
+ b.updateUI();
+ } else
+ applyStyle( b, style );
+ b.revalidate();
+ b.repaint();
+ break;
}
}
+ /**
+ * @since 2
+ */
+ protected void applyStyle( AbstractButton b, Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style,
+ (key, value) -> applyStyleProperty( b, key, value ) );
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( AbstractButton b, String key, Object value ) {
+ if( key.startsWith( "help." ) ) {
+ if( !(helpButtonIcon instanceof FlatHelpButtonIcon) )
+ return new UnknownStyleException( key );
+
+ if( helpButtonIconShared ) {
+ helpButtonIcon = FlatStylingSupport.cloneIcon( helpButtonIcon );
+ helpButtonIconShared = false;
+ }
+
+ key = key.substring( "help.".length() );
+ return ((FlatHelpButtonIcon)helpButtonIcon).applyStyleProperty( key, value );
+ }
+
+ if( borderShared == null )
+ borderShared = new AtomicBoolean( true );
+ return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, b, borderShared );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ Map> infos = FlatStylingSupport.getAnnotatedStyleableInfos( this, c.getBorder() );
+ if( helpButtonIcon instanceof FlatHelpButtonIcon )
+ FlatStylingSupport.putAllPrefixKey( infos, "help.", ((FlatHelpButtonIcon)helpButtonIcon).getStyleableInfos() );
+ return infos;
+ }
+
static boolean isContentAreaFilled( Component c ) {
return !(c instanceof AbstractButton) || ((AbstractButton)c).isContentAreaFilled();
}
@@ -339,7 +416,8 @@ public class FlatButtonUI
// paint shadow
Color shadowColor = def ? defaultShadowColor : this.shadowColor;
- if( shadowColor != null && shadowWidth > 0 && focusWidth > 0 && c.isEnabled() &&
+ if( paintShadow &&
+ shadowColor != null && shadowWidth > 0 && focusWidth > 0 && c.isEnabled() &&
!isToolBarButton && !isBorderlessButton( c ) &&
!(isFocusPainted( c ) && FlatUIUtils.isPermanentFocusOwner( c )) )
{
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatCheckBoxMenuItemUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatCheckBoxMenuItemUI.java
index 5bcb2f88..b899d7b3 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatCheckBoxMenuItemUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatCheckBoxMenuItemUI.java
@@ -18,11 +18,15 @@ package com.formdev.flatlaf.ui;
import java.awt.Dimension;
import java.awt.Graphics;
+import java.beans.PropertyChangeListener;
+import java.util.Map;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JCheckBoxMenuItem}.
@@ -54,13 +58,22 @@ import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
*/
public class FlatCheckBoxMenuItemUI
extends BasicCheckBoxMenuItemUI
+ implements StyleableUI
{
private FlatMenuItemRenderer renderer;
+ private Map oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
return new FlatCheckBoxMenuItemUI();
}
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( menuItem ) );
+ }
+
@Override
protected void installDefaults() {
super.installDefaults();
@@ -75,12 +88,46 @@ public class FlatCheckBoxMenuItemUI
super.uninstallDefaults();
renderer = null;
+ oldStyleValues = null;
}
protected FlatMenuItemRenderer createRenderer() {
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
}
+ @Override
+ protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
+ return FlatStylingSupport.createPropertyChangeListener( c, this::applyStyle, super.createPropertyChangeListener( c ) );
+ }
+
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ try {
+ return renderer.applyStyleProperty( key, value );
+ } catch ( UnknownStyleException ex ) {
+ // ignore
+ }
+
+ return FlatMenuItemUI.applyStyleProperty( this, menuItem, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatMenuItemUI.getStyleableInfos( renderer );
+ }
+
@Override
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
return renderer.getPreferredMenuItemSize();
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatCheckBoxUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatCheckBoxUI.java
index e545fa66..5d6f5526 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatCheckBoxUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatCheckBoxUI.java
@@ -43,7 +43,16 @@ public class FlatCheckBoxUI
extends FlatRadioButtonUI
{
public static ComponentUI createUI( JComponent c ) {
- return FlatUIUtils.createSharedUI( FlatCheckBoxUI.class, FlatCheckBoxUI::new );
+ return FlatUIUtils.canUseSharedUI( c )
+ ? FlatUIUtils.createSharedUI( FlatCheckBoxUI.class, () -> new FlatCheckBoxUI( true ) )
+ : new FlatCheckBoxUI( false );
+ }
+
+ /**
+ * @since 2
+ */
+ protected FlatCheckBoxUI( boolean shared ) {
+ super( shared );
}
@Override
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatComboBoxUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatComboBoxUI.java
index 392efd52..6982a601 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatComboBoxUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatComboBoxUI.java
@@ -41,6 +41,9 @@ import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeListener;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.CellRendererPane;
@@ -67,6 +70,8 @@ import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.ComboPopup;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.FlatClientProperties;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.SystemInfo;
/**
@@ -108,29 +113,30 @@ import com.formdev.flatlaf.util.SystemInfo;
*/
public class FlatComboBoxUI
extends BasicComboBoxUI
+ implements StyleableUI
{
- protected int minimumWidth;
- protected int editorColumns;
- protected String buttonStyle;
- protected String arrowType;
+ @Styleable protected int minimumWidth;
+ @Styleable protected int editorColumns;
+ @Styleable protected String buttonStyle;
+ @Styleable protected String arrowType;
protected boolean isIntelliJTheme;
- protected Color borderColor;
- protected Color disabledBorderColor;
+ @Styleable protected Color borderColor;
+ @Styleable protected Color disabledBorderColor;
- protected Color editableBackground;
- protected Color focusedBackground;
- protected Color disabledBackground;
- protected Color disabledForeground;
+ @Styleable protected Color editableBackground;
+ @Styleable protected Color focusedBackground;
+ @Styleable protected Color disabledBackground;
+ @Styleable protected Color disabledForeground;
- protected Color buttonBackground;
- protected Color buttonEditableBackground;
- protected Color buttonFocusedBackground;
- protected Color buttonArrowColor;
- protected Color buttonDisabledArrowColor;
- protected Color buttonHoverArrowColor;
- protected Color buttonPressedArrowColor;
+ @Styleable protected Color buttonBackground;
+ @Styleable protected Color buttonEditableBackground;
+ @Styleable protected Color buttonFocusedBackground;
+ @Styleable protected Color buttonArrowColor;
+ @Styleable protected Color buttonDisabledArrowColor;
+ @Styleable protected Color buttonHoverArrowColor;
+ @Styleable protected Color buttonPressedArrowColor;
- protected Color popupBackground;
+ @Styleable protected Color popupBackground;
private MouseListener hoverListener;
protected boolean hover;
@@ -138,10 +144,20 @@ public class FlatComboBoxUI
private CellPaddingBorder paddingBorder;
+ private Map oldStyleValues;
+ private AtomicBoolean borderShared;
+
public static ComponentUI createUI( JComponent c ) {
return new FlatComboBoxUI();
}
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( comboBox ) );
+ }
+
@Override
protected void installListeners() {
super.installListeners();
@@ -250,6 +266,9 @@ public class FlatComboBoxUI
paddingBorder.uninstall();
+ oldStyleValues = null;
+ borderShared = null;
+
MigLayoutVisualPadding.uninstall( comboBox );
}
@@ -328,6 +347,11 @@ public class FlatComboBoxUI
comboBox.repaint();
else if( FlatClientProperties.MINIMUM_WIDTH.equals( propertyName ) )
comboBox.revalidate();
+ else if( FlatClientProperties.STYLE.equals( propertyName ) ) {
+ applyStyle( e.getNewValue() );
+ comboBox.revalidate();
+ comboBox.repaint();
+ }
};
}
@@ -414,6 +438,55 @@ public class FlatComboBoxUI
return new FlatComboBoxButton();
}
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ Insets oldPadding = padding;
+ int oldEditorColumns = editorColumns;
+
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+
+ if( !padding.equals( oldPadding ) ) {
+ paddingBorder.padding = padding;
+ updateEditorPadding();
+ }
+ if( arrowButton instanceof FlatComboBoxButton )
+ ((FlatComboBoxButton)arrowButton).updateStyle();
+ if( popup instanceof FlatComboPopup )
+ ((FlatComboPopup)popup).updateStyle();
+ if( editorColumns != oldEditorColumns && editor instanceof JTextField )
+ ((JTextField)editor).setColumns( editorColumns );
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ // BasicComboBoxUI
+ if( key.equals( "padding" ) ) {
+ Object oldValue = padding;
+ padding = (Insets) value;
+ return oldValue;
+ }
+
+ if( borderShared == null )
+ borderShared = new AtomicBoolean( true );
+ return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, comboBox, borderShared );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ Map> infos = new LinkedHashMap<>();
+ infos.put( "padding", Insets.class );
+ FlatStylingSupport.collectAnnotatedStyleableInfos( this, infos );
+ FlatStylingSupport.collectStyleableInfos( comboBox.getBorder(), infos );
+ return infos;
+ }
+
@Override
public void update( Graphics g, JComponent c ) {
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
@@ -612,6 +685,11 @@ public class FlatComboBoxUI
hoverForeground, hoverBackground, pressedForeground, pressedBackground );
}
+ protected void updateStyle() {
+ updateStyle( arrowType, buttonArrowColor, buttonDisabledArrowColor,
+ buttonHoverArrowColor, null, buttonPressedArrowColor, null );
+ }
+
@Override
protected boolean isHover() {
return super.isHover() || (!comboBox.isEditable() ? hover : false);
@@ -708,6 +786,10 @@ public class FlatComboBoxUI
super.configureList();
list.setCellRenderer( new PopupListCellRenderer() );
+ updateStyle();
+ }
+
+ void updateStyle() {
if( popupBackground != null )
list.setBackground( popupBackground );
}
@@ -788,7 +870,7 @@ public class FlatComboBoxUI
private static class CellPaddingBorder
extends AbstractBorder
{
- private final Insets padding;
+ private Insets padding;
private JComponent rendererComponent;
private Border rendererBorder;
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatDropShadowBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatDropShadowBorder.java
index 505b9276..2f8df4dc 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatDropShadowBorder.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatDropShadowBorder.java
@@ -24,6 +24,9 @@ import java.awt.Image;
import java.awt.Insets;
import java.awt.RadialGradientPaint;
import java.awt.image.BufferedImage;
+import java.util.Map;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.UIScale;
@@ -40,14 +43,17 @@ import com.formdev.flatlaf.util.UIScale;
*/
public class FlatDropShadowBorder
extends FlatEmptyBorder
+ implements StyleableBorder
{
- private final Color shadowColor;
- private final Insets shadowInsets;
- private final float shadowOpacity;
+ @Styleable protected Color shadowColor;
+ @Styleable protected Insets shadowInsets;
+ @Styleable protected float shadowOpacity;
- private final int shadowSize;
+ private int shadowSize;
private Image shadowImage;
private Color lastShadowColor;
+ private float lastShadowOpacity;
+ private int lastShadowSize;
private double lastSystemScaleFactor;
private float lastUserScaleFactor;
@@ -64,17 +70,47 @@ public class FlatDropShadowBorder
}
public FlatDropShadowBorder( Color shadowColor, Insets shadowInsets, float shadowOpacity ) {
- super( Math.max( shadowInsets.top, 0 ), Math.max( shadowInsets.left, 0 ),
- Math.max( shadowInsets.bottom, 0 ), Math.max( shadowInsets.right, 0 ) );
+ super( nonNegativeInsets( shadowInsets ) );
+
this.shadowColor = shadowColor;
this.shadowInsets = shadowInsets;
this.shadowOpacity = shadowOpacity;
- shadowSize = Math.max(
+ shadowSize = maxInset( shadowInsets );
+ }
+
+ private static Insets nonNegativeInsets( Insets shadowInsets ) {
+ return new Insets( Math.max( shadowInsets.top, 0 ), Math.max( shadowInsets.left, 0 ),
+ Math.max( shadowInsets.bottom, 0 ), Math.max( shadowInsets.right, 0 ) );
+ }
+
+ private int maxInset( Insets shadowInsets ) {
+ return Math.max(
Math.max( shadowInsets.left, shadowInsets.right ),
Math.max( shadowInsets.top, shadowInsets.bottom ) );
}
+ /**
+ * @since 2
+ */
+ @Override
+ public Object applyStyleProperty( String key, Object value ) {
+ Object oldValue = FlatStylingSupport.applyToAnnotatedObject( this, key, value );
+ if( key.equals( "shadowInsets" ) ) {
+ applyStyleProperty( nonNegativeInsets( shadowInsets ) );
+ shadowSize = maxInset( shadowInsets );
+ }
+ return oldValue;
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos() {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
+
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
if( shadowSize <= 0 )
@@ -91,12 +127,16 @@ public class FlatDropShadowBorder
float userScaleFactor = UIScale.getUserScaleFactor();
if( shadowImage == null ||
!shadowColor.equals( lastShadowColor ) ||
+ lastShadowOpacity != shadowOpacity ||
+ lastShadowSize != shadowSize ||
lastSystemScaleFactor != scaleFactor ||
lastUserScaleFactor != userScaleFactor )
{
shadowImage = createShadowImage( shadowColor, shadowSize, shadowOpacity,
(float) (scaleFactor * userScaleFactor) );
lastShadowColor = shadowColor;
+ lastShadowOpacity = shadowOpacity;
+ lastShadowSize = shadowSize;
lastSystemScaleFactor = scaleFactor;
lastUserScaleFactor = userScaleFactor;
}
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatEditorPaneUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatEditorPaneUI.java
index 61bbcaf2..33a1a381 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatEditorPaneUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatEditorPaneUI.java
@@ -24,6 +24,8 @@ import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.event.FocusListener;
import java.beans.PropertyChangeEvent;
+import java.util.Map;
+import java.util.function.Consumer;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.UIManager;
@@ -31,6 +33,8 @@ import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicEditorPaneUI;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.FlatClientProperties;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
/**
@@ -61,20 +65,35 @@ import com.formdev.flatlaf.util.HiDPIUtils;
*/
public class FlatEditorPaneUI
extends BasicEditorPaneUI
+ implements StyleableUI
{
- protected int minimumWidth;
+ @Styleable protected int minimumWidth;
protected boolean isIntelliJTheme;
- protected Color focusedBackground;
+ private Color background;
+ @Styleable protected Color disabledBackground;
+ @Styleable protected Color inactiveBackground;
+ @Styleable protected Color focusedBackground;
+
+ private Color oldDisabledBackground;
+ private Color oldInactiveBackground;
private Insets defaultMargin;
private Object oldHonorDisplayProperties;
private FocusListener focusListener;
+ private Map oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
return new FlatEditorPaneUI();
}
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( c ) );
+ }
+
@Override
protected void installDefaults() {
super.installDefaults();
@@ -82,6 +101,9 @@ public class FlatEditorPaneUI
String prefix = getPropertyPrefix();
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
+ background = UIManager.getColor( prefix + ".background" );
+ disabledBackground = UIManager.getColor( prefix + ".disabledBackground" );
+ inactiveBackground = UIManager.getColor( prefix + ".inactiveBackground" );
focusedBackground = UIManager.getColor( prefix + ".focusedBackground" );
defaultMargin = UIManager.getInsets( prefix + ".margin" );
@@ -95,8 +117,16 @@ public class FlatEditorPaneUI
protected void uninstallDefaults() {
super.uninstallDefaults();
+ background = null;
+ disabledBackground = null;
+ inactiveBackground = null;
focusedBackground = null;
+ oldDisabledBackground = null;
+ oldInactiveBackground = null;
+
+ oldStyleValues = null;
+
getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, oldHonorDisplayProperties );
}
@@ -119,18 +149,62 @@ public class FlatEditorPaneUI
@Override
protected void propertyChange( PropertyChangeEvent e ) {
+ // invoke updateBackground() before super.propertyChange()
+ String propertyName = e.getPropertyName();
+ if( "editable".equals( propertyName ) || "enabled".equals( propertyName ) )
+ updateBackground();
+
super.propertyChange( e );
- propertyChange( getComponent(), e );
+ propertyChange( getComponent(), e, this::applyStyle );
}
- static void propertyChange( JTextComponent c, PropertyChangeEvent e ) {
+ static void propertyChange( JTextComponent c, PropertyChangeEvent e, Consumer applyStyle ) {
switch( e.getPropertyName() ) {
case FlatClientProperties.MINIMUM_WIDTH:
c.revalidate();
break;
+
+ case FlatClientProperties.STYLE:
+ applyStyle.accept( e.getNewValue() );
+ c.revalidate();
+ c.repaint();
+ break;
}
}
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldDisabledBackground = disabledBackground;
+ oldInactiveBackground = inactiveBackground;
+
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+
+ updateBackground();
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, getComponent(), key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
+
+ private void updateBackground() {
+ FlatTextFieldUI.updateBackground( getComponent(), background,
+ disabledBackground, inactiveBackground,
+ oldDisabledBackground, oldInactiveBackground );
+ }
+
@Override
public Dimension getPreferredSize( JComponent c ) {
return applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth, defaultMargin );
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatEmptyBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatEmptyBorder.java
index 55d08c72..34e5fb04 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatEmptyBorder.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatEmptyBorder.java
@@ -50,6 +50,12 @@ public class FlatEmptyBorder
@Override
public Insets getBorderInsets( Component c, Insets insets ) {
+ return scaleInsets( c, insets, top, left, bottom, right );
+ }
+
+ protected static Insets scaleInsets( Component c, Insets insets,
+ int top, int left, int bottom, int right )
+ {
boolean leftToRight = left == right || c.getComponentOrientation().isLeftToRight();
insets.left = scale( leftToRight ? left : right );
insets.top = scale( top );
@@ -61,4 +67,13 @@ public class FlatEmptyBorder
public Insets getUnscaledBorderInsets() {
return super.getBorderInsets();
}
+
+ public Object applyStyleProperty( Insets insets ) {
+ Insets oldInsets = getUnscaledBorderInsets();
+ top = insets.top;
+ left = insets.left;
+ bottom = insets.bottom;
+ right = insets.right;
+ return oldInsets;
+ }
}
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatInternalFrameUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatInternalFrameUI.java
index f53b3d87..834ebfc5 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatInternalFrameUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatInternalFrameUI.java
@@ -24,6 +24,10 @@ import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeListener;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.JComponent;
import javax.swing.JInternalFrame;
import javax.swing.LookAndFeel;
@@ -31,6 +35,9 @@ import javax.swing.UIManager;
import javax.swing.event.MouseInputAdapter;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicInternalFrameUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JInternalFrame}.
@@ -86,9 +93,13 @@ import javax.swing.plaf.basic.BasicInternalFrameUI;
*/
public class FlatInternalFrameUI
extends BasicInternalFrameUI
+ implements StyleableUI
{
protected FlatWindowResizer windowResizer;
+ private Map oldStyleValues;
+ private AtomicBoolean borderShared;
+
public static ComponentUI createUI( JComponent c ) {
return new FlatInternalFrameUI( (JInternalFrame) c );
}
@@ -104,6 +115,8 @@ public class FlatInternalFrameUI
LookAndFeel.installProperty( frame, "opaque", false );
windowResizer = createWindowResizer();
+
+ applyStyle( FlatStylingSupport.getStyle( c ) );
}
@Override
@@ -114,6 +127,9 @@ public class FlatInternalFrameUI
windowResizer.uninstall();
windowResizer = null;
}
+
+ oldStyleValues = null;
+ borderShared = null;
}
@Override
@@ -130,15 +146,46 @@ public class FlatInternalFrameUI
return new FlatBorderListener();
}
+ @Override
+ protected PropertyChangeListener createPropertyChangeListener() {
+ return FlatStylingSupport.createPropertyChangeListener( frame, this::applyStyle,
+ super.createPropertyChangeListener() );
+ }
+
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ if( borderShared == null )
+ borderShared = new AtomicBoolean( true );
+ return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, frame, borderShared );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this, frame.getBorder() );
+ }
+
//---- class FlatInternalFrameBorder --------------------------------------
public static class FlatInternalFrameBorder
extends FlatEmptyBorder
+ implements StyleableBorder
{
- private final Color activeBorderColor = UIManager.getColor( "InternalFrame.activeBorderColor" );
- private final Color inactiveBorderColor = UIManager.getColor( "InternalFrame.inactiveBorderColor" );
- private final int borderLineWidth = FlatUIUtils.getUIInt( "InternalFrame.borderLineWidth", 1 );
- private final boolean dropShadowPainted = UIManager.getBoolean( "InternalFrame.dropShadowPainted" );
+ @Styleable protected Color activeBorderColor = UIManager.getColor( "InternalFrame.activeBorderColor" );
+ @Styleable protected Color inactiveBorderColor = UIManager.getColor( "InternalFrame.inactiveBorderColor" );
+ @Styleable protected int borderLineWidth = FlatUIUtils.getUIInt( "InternalFrame.borderLineWidth", 1 );
+ @Styleable protected boolean dropShadowPainted = UIManager.getBoolean( "InternalFrame.dropShadowPainted" );
private final FlatDropShadowBorder activeDropShadowBorder = new FlatDropShadowBorder(
UIManager.getColor( "InternalFrame.activeDropShadowColor" ),
@@ -153,6 +200,36 @@ public class FlatInternalFrameUI
super( UIManager.getInsets( "InternalFrame.borderMargins" ) );
}
+ @Override
+ public Object applyStyleProperty( String key, Object value ) {
+ switch( key ) {
+ case "borderMargins": return applyStyleProperty( (Insets) value );
+
+ case "activeDropShadowColor": return activeDropShadowBorder.applyStyleProperty( "shadowColor", value );
+ case "activeDropShadowInsets": return activeDropShadowBorder.applyStyleProperty( "shadowInsets", value );
+ case "activeDropShadowOpacity": return activeDropShadowBorder.applyStyleProperty( "shadowOpacity", value );
+ case "inactiveDropShadowColor": return inactiveDropShadowBorder.applyStyleProperty( "shadowColor", value );
+ case "inactiveDropShadowInsets": return inactiveDropShadowBorder.applyStyleProperty( "shadowInsets", value );
+ case "inactiveDropShadowOpacity": return inactiveDropShadowBorder.applyStyleProperty( "shadowOpacity", value );
+ }
+
+ return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
+ }
+
+ @Override
+ public Map> getStyleableInfos() {
+ Map> infos = new LinkedHashMap<>();
+ FlatStylingSupport.collectAnnotatedStyleableInfos( this, infos );
+ infos.put( "borderMargins", Insets.class );
+ infos.put( "activeDropShadowColor", Color.class );
+ infos.put( "activeDropShadowInsets", Insets.class );
+ infos.put( "activeDropShadowOpacity", float.class );
+ infos.put( "inactiveDropShadowColor", Color.class );
+ infos.put( "inactiveDropShadowInsets", Insets.class );
+ infos.put( "inactiveDropShadowOpacity", float.class );
+ return infos;
+ }
+
@Override
public Insets getBorderInsets( Component c, Insets insets ) {
if( c instanceof JInternalFrame && ((JInternalFrame)c).isMaximum() ) {
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatLabelUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatLabelUI.java
index ae629b55..6334cd6e 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatLabelUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatLabelUI.java
@@ -24,6 +24,7 @@ import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
import javax.swing.Icon;
import javax.swing.JComponent;
@@ -33,7 +34,10 @@ import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.basic.BasicLabelUI;
+import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.UIScale;
@@ -54,13 +58,32 @@ import com.formdev.flatlaf.util.UIScale;
*/
public class FlatLabelUI
extends BasicLabelUI
+ implements StyleableUI
{
- private Color disabledForeground;
+ @Styleable protected Color disabledForeground;
+ private final boolean shared;
private boolean defaults_initialized = false;
+ private Map oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
- return FlatUIUtils.createSharedUI( FlatLabelUI.class, FlatLabelUI::new );
+ return FlatUIUtils.canUseSharedUI( c )
+ ? FlatUIUtils.createSharedUI( FlatLabelUI.class, () -> new FlatLabelUI( true ) )
+ : new FlatLabelUI( false );
+ }
+
+ /**
+ * @since 2
+ */
+ protected FlatLabelUI( boolean shared ) {
+ this.shared = shared;
+ }
+
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( (JLabel) c, FlatStylingSupport.getStyle( c ) );
}
@Override
@@ -77,7 +100,9 @@ public class FlatLabelUI
@Override
protected void uninstallDefaults( JLabel c ) {
super.uninstallDefaults( c );
+
defaults_initialized = false;
+ oldStyleValues = null;
}
@Override
@@ -94,10 +119,44 @@ public class FlatLabelUI
if( name == "text" || name == "font" || name == "foreground" ) {
JLabel label = (JLabel) e.getSource();
updateHTMLRenderer( label, label.getText(), true );
+ } else if( name.equals( FlatClientProperties.STYLE ) ) {
+ JLabel label = (JLabel) e.getSource();
+ Object style = e.getNewValue();
+ if( style != null && shared ) {
+ // unshare component UI if necessary
+ // updateUI() invokes applyStyle() from installUI()
+ label.updateUI();
+ } else
+ applyStyle( label, style );
+ label.revalidate();
+ label.repaint();
} else
super.propertyChange( e );
}
+ /**
+ * @since 2
+ */
+ protected void applyStyle( JLabel c, Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style,
+ (key, value) -> applyStyleProperty( c, key, value ) );
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( JLabel c, String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, c, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
+
/**
* Checks whether text contains HTML tags that use "absolute-size" keywords
* (e.g. "x-large") for font-size in default style sheet
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatLineBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatLineBorder.java
index a529f096..d8e3ea69 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatLineBorder.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatLineBorder.java
@@ -61,8 +61,8 @@ public class FlatLineBorder
Graphics2D g2 = (Graphics2D) g.create();
try {
FlatUIUtils.setRenderingHints( g2 );
- g2.setColor( lineColor );
- FlatUIUtils.paintComponentBorder( g2, x, y, width, height, 0f, scale( lineThickness ), 0f );
+ g2.setColor( getLineColor() );
+ FlatUIUtils.paintComponentBorder( g2, x, y, width, height, 0f, scale( getLineThickness() ), 0f );
} finally {
g2.dispose();
}
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatListCellBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatListCellBorder.java
index 16d3a23d..4e87570e 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatListCellBorder.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatListCellBorder.java
@@ -16,11 +16,15 @@
package com.formdev.flatlaf.ui;
+import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
+import java.awt.Insets;
+import java.util.function.Function;
import javax.swing.JList;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
+import javax.swing.plaf.ListUI;
/**
* Cell border for {@link javax.swing.DefaultListCellRenderer}
@@ -33,12 +37,54 @@ import javax.swing.UIManager;
public class FlatListCellBorder
extends FlatLineBorder
{
- final boolean showCellFocusIndicator = UIManager.getBoolean( "List.showCellFocusIndicator" );
+ protected boolean showCellFocusIndicator = UIManager.getBoolean( "List.showCellFocusIndicator" );
+
+ private Component c;
protected FlatListCellBorder() {
super( UIManager.getInsets( "List.cellMargins" ), UIManager.getColor( "List.cellFocusColor" ) );
}
+ @Override
+ public Insets getBorderInsets( Component c, Insets insets ) {
+ Insets m = getStyleFromListUI( c, ui -> ui.cellMargins );
+ if( m != null )
+ return scaleInsets( c, insets, m.top, m.left, m.bottom, m.right );
+
+ return super.getBorderInsets( c, insets );
+ }
+
+ @Override
+ public Color getLineColor() {
+ if( c != null ) {
+ Color color = getStyleFromListUI( c, ui -> ui.cellFocusColor );
+ if( color != null )
+ return color;
+ }
+ return super.getLineColor();
+ }
+
+ @Override
+ public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
+ this.c = c;
+ super.paintBorder( c, g, x, y, width, height );
+ this.c = null;
+ }
+
+ /**
+ * Because this borders are always shared for all lists,
+ * get border specific style from FlatListUI.
+ */
+ static T getStyleFromListUI( Component c, Function f ) {
+ JList> list = (JList>) SwingUtilities.getAncestorOfClass( JList.class, c );
+ if( list != null ) {
+ ListUI ui = list.getUI();
+ if( ui instanceof FlatListUI )
+ return f.apply( (FlatListUI) ui );
+ }
+ return null;
+ }
+
//---- class Default ------------------------------------------------------
/**
@@ -74,6 +120,8 @@ public class FlatListCellBorder
{
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
+ Boolean b = getStyleFromListUI( c, ui -> ui.showCellFocusIndicator );
+ boolean showCellFocusIndicator = (b != null) ? b : this.showCellFocusIndicator;
if( !showCellFocusIndicator )
return;
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatListUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatListUI.java
index 5c1fe585..1d9821b8 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatListUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatListUI.java
@@ -18,14 +18,18 @@ package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.EventQueue;
+import java.awt.Insets;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.beans.PropertyChangeListener;
+import java.util.Map;
import javax.swing.JComponent;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicListUI;
import com.formdev.flatlaf.FlatClientProperties;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JList}.
@@ -65,16 +69,31 @@ import com.formdev.flatlaf.FlatClientProperties;
*/
public class FlatListUI
extends BasicListUI
+ implements StyleableUI
{
- protected Color selectionBackground;
- protected Color selectionForeground;
- protected Color selectionInactiveBackground;
- protected Color selectionInactiveForeground;
+ @Styleable protected Color selectionBackground;
+ @Styleable protected Color selectionForeground;
+ @Styleable protected Color selectionInactiveBackground;
+ @Styleable protected Color selectionInactiveForeground;
+
+ // for FlatListCellBorder
+ @Styleable protected Insets cellMargins;
+ @Styleable protected Color cellFocusColor;
+ @Styleable protected boolean showCellFocusIndicator;
+
+ private Map oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
return new FlatListUI();
}
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( c ) );
+ }
+
@Override
protected void installDefaults() {
super.installDefaults();
@@ -95,17 +114,8 @@ public class FlatListUI
selectionForeground = null;
selectionInactiveBackground = null;
selectionInactiveForeground = null;
- }
- @Override
- protected PropertyChangeListener createPropertyChangeListener() {
- PropertyChangeListener superListener = super.createPropertyChangeListener();
- return e -> {
- superListener.propertyChange( e );
-
- if( FlatClientProperties.COMPONENT_FOCUS_OWNER.equals( e.getPropertyName() ) )
- toggleSelectionColors();
- };
+ oldStyleValues = null;
}
@Override
@@ -129,6 +139,71 @@ public class FlatListUI
};
}
+ @Override
+ protected PropertyChangeListener createPropertyChangeListener() {
+ PropertyChangeListener superListener = super.createPropertyChangeListener();
+ return e -> {
+ superListener.propertyChange( e );
+
+ switch( e.getPropertyName() ) {
+ case FlatClientProperties.COMPONENT_FOCUS_OWNER:
+ toggleSelectionColors();
+ break;
+
+ case FlatClientProperties.STYLE:
+ applyStyle( e.getNewValue() );
+ list.revalidate();
+ list.repaint();
+ break;
+ }
+ };
+ }
+
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ Color oldSelectionBackground = selectionBackground;
+ Color oldSelectionForeground = selectionForeground;
+ Color oldSelectionInactiveBackground = selectionInactiveBackground;
+ Color oldSelectionInactiveForeground = selectionInactiveForeground;
+
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+
+ // update selection background
+ if( selectionBackground != oldSelectionBackground ) {
+ Color selBg = list.getSelectionBackground();
+ if( selBg == oldSelectionBackground )
+ list.setSelectionBackground( selectionBackground );
+ else if( selBg == oldSelectionInactiveBackground )
+ list.setSelectionBackground( selectionInactiveBackground );
+ }
+
+ // update selection foreground
+ if( selectionForeground != oldSelectionForeground ) {
+ Color selFg = list.getSelectionForeground();
+ if( selFg == oldSelectionForeground )
+ list.setSelectionForeground( selectionForeground );
+ else if( selFg == oldSelectionInactiveForeground )
+ list.setSelectionForeground( selectionInactiveForeground );
+ }
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, list, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
+
/**
* Toggle selection colors from focused to inactive and vice versa.
*
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMarginBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMarginBorder.java
index 27240fef..36b58f00 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMarginBorder.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMarginBorder.java
@@ -29,7 +29,7 @@ import javax.swing.plaf.basic.BasicBorders;
public class FlatMarginBorder
extends BasicBorders.MarginBorder
{
- private final int left, right, top, bottom;
+ protected int left, right, top, bottom;
public FlatMarginBorder() {
left = right = top = bottom = 0;
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarBorder.java
index 5701f935..6331e035 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarBorder.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarBorder.java
@@ -21,8 +21,11 @@ import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Insets;
+import java.util.Map;
import javax.swing.JMenuBar;
import javax.swing.UIManager;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
/**
* Border for {@link javax.swing.JMenuBar}.
@@ -33,8 +36,22 @@ import javax.swing.UIManager;
*/
public class FlatMenuBarBorder
extends FlatMarginBorder
+ implements StyleableBorder
{
- private final Color borderColor = UIManager.getColor( "MenuBar.borderColor" );
+ @Styleable protected Color borderColor = UIManager.getColor( "MenuBar.borderColor" );
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Object applyStyleProperty( String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
+ }
+
+ @Override
+ public Map> getStyleableInfos() {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarUI.java
index d5e34fe7..39fb218b 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarUI.java
@@ -18,8 +18,12 @@ package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Graphics;
+import java.awt.Insets;
import java.awt.Window;
import java.awt.event.ActionEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.JComponent;
@@ -35,7 +39,10 @@ import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicMenuBarUI;
+import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.SystemInfo;
/**
@@ -47,13 +54,30 @@ import com.formdev.flatlaf.util.SystemInfo;
* @uiDefault MenuBar.background Color
* @uiDefault MenuBar.foreground Color
* @uiDefault MenuBar.border Border
+ *
+ *
+ *
* @uiDefault TitlePane.unifiedBackground boolean
*
* @author Karl Tauber
*/
public class FlatMenuBarUI
extends BasicMenuBarUI
+ implements StyleableUI
{
+ // used in FlatMenuItemBorder
+ /** @since 2 */ @Styleable protected Insets itemMargins;
+
+ // used in FlatMenuUI
+ /** @since 2 */ @Styleable protected Color hoverBackground;
+ /** @since 2 */ @Styleable protected Color underlineSelectionBackground;
+ /** @since 2 */ @Styleable protected Color underlineSelectionColor;
+ /** @since 2 */ @Styleable protected int underlineSelectionHeight = -1;
+
+ private PropertyChangeListener propertyChangeListener;
+ private Map oldStyleValues;
+ private AtomicBoolean borderShared;
+
public static ComponentUI createUI( JComponent c ) {
return new FlatMenuBarUI();
}
@@ -63,6 +87,13 @@ public class FlatMenuBarUI
* Do not add any functionality here.
*/
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( c ) );
+ }
+
@Override
protected void installDefaults() {
super.installDefaults();
@@ -70,6 +101,31 @@ public class FlatMenuBarUI
LookAndFeel.installProperty( menuBar, "opaque", false );
}
+ @Override
+ protected void uninstallDefaults() {
+ super.uninstallDefaults();
+
+ oldStyleValues = null;
+ borderShared = null;
+ }
+
+ @Override
+ protected void installListeners() {
+ super.installListeners();
+
+ propertyChangeListener = FlatStylingSupport.createPropertyChangeListener(
+ menuBar, this::applyStyle, null );
+ menuBar.addPropertyChangeListener( FlatClientProperties.STYLE, propertyChangeListener );
+ }
+
+ @Override
+ protected void uninstallListeners() {
+ super.uninstallListeners();
+
+ menuBar.removePropertyChangeListener( FlatClientProperties.STYLE, propertyChangeListener );
+ propertyChangeListener = null;
+ }
+
@Override
protected void installKeyboardActions() {
super.installKeyboardActions();
@@ -82,6 +138,30 @@ public class FlatMenuBarUI
map.put( "takeFocus", new TakeFocus() );
}
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ if( borderShared == null )
+ borderShared = new AtomicBoolean( true );
+ return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, menuBar, borderShared );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this, menuBar.getBorder() );
+ }
+
@Override
public void update( Graphics g, JComponent c ) {
// paint background
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemBorder.java
index aa8079a6..108371ec 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemBorder.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemBorder.java
@@ -18,9 +18,11 @@ package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Component;
+import java.awt.Container;
import java.awt.Insets;
import javax.swing.JMenuBar;
import javax.swing.UIManager;
+import javax.swing.plaf.MenuBarUI;
/**
* Border for {@link javax.swing.JMenu}, {@link javax.swing.JMenuItem},
@@ -33,15 +35,22 @@ import javax.swing.UIManager;
public class FlatMenuItemBorder
extends FlatMarginBorder
{
+ // only used if parent menubar is not a instance of FlatMenuBarUI
private final Insets menuBarItemMargins = UIManager.getInsets( "MenuBar.itemMargins" );
@Override
public Insets getBorderInsets( Component c, Insets insets ) {
- if( c.getParent() instanceof JMenuBar ) {
- insets.top = scale( menuBarItemMargins.top );
- insets.left = scale( menuBarItemMargins.left );
- insets.bottom = scale( menuBarItemMargins.bottom );
- insets.right = scale( menuBarItemMargins.right );
+ Container parent = c.getParent();
+ if( parent instanceof JMenuBar ) {
+ // get margins from FlatMenuBarUI to allow styling
+ MenuBarUI ui = ((JMenuBar)parent).getUI();
+ Insets margins = (ui instanceof FlatMenuBarUI && ((FlatMenuBarUI)ui).itemMargins != null)
+ ? ((FlatMenuBarUI)ui).itemMargins
+ : this.menuBarItemMargins;
+ insets.top = scale( margins.top );
+ insets.left = scale( margins.left );
+ insets.bottom = scale( margins.bottom );
+ insets.right = scale( margins.right );
return insets;
} else
return super.getBorderInsets( c, insets );
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemRenderer.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemRenderer.java
index 3d4deb21..1886d08d 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemRenderer.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemRenderer.java
@@ -30,6 +30,7 @@ import java.awt.Rectangle;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.text.AttributedCharacterIterator;
+import java.util.Map;
import javax.swing.Icon;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
@@ -39,6 +40,10 @@ import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.View;
import com.formdev.flatlaf.FlatLaf;
+import com.formdev.flatlaf.icons.FlatCheckBoxMenuItemIcon;
+import com.formdev.flatlaf.icons.FlatMenuArrowIcon;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.DerivedColor;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.HiDPIUtils;
@@ -57,33 +62,32 @@ import com.formdev.flatlaf.util.SystemInfo;
* @uiDefault MenuItem.underlineSelectionCheckBackground Color
* @uiDefault MenuItem.underlineSelectionColor Color
* @uiDefault MenuItem.underlineSelectionHeight int
- * @uiDefault MenuItem.selectionBackground Color
*
* @author Karl Tauber
*/
public class FlatMenuItemRenderer
{
protected final JMenuItem menuItem;
- protected final Icon checkIcon;
- protected final Icon arrowIcon;
+ protected Icon checkIcon;
+ protected Icon arrowIcon;
protected final Font acceleratorFont;
protected final String acceleratorDelimiter;
- protected final int minimumWidth = UIManager.getInt( "MenuItem.minimumWidth" );
- protected final Dimension minimumIconSize;
- protected final int textAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textAcceleratorGap", 28 );
- protected final int textNoAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textNoAcceleratorGap", 6 );
- protected final int acceleratorArrowGap = FlatUIUtils.getUIInt( "MenuItem.acceleratorArrowGap", 2 );
+ @Styleable protected int minimumWidth = UIManager.getInt( "MenuItem.minimumWidth" );
+ @Styleable protected Dimension minimumIconSize;
+ @Styleable protected int textAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textAcceleratorGap", 28 );
+ @Styleable protected int textNoAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textNoAcceleratorGap", 6 );
+ @Styleable protected int acceleratorArrowGap = FlatUIUtils.getUIInt( "MenuItem.acceleratorArrowGap", 2 );
- protected final Color checkBackground = UIManager.getColor( "MenuItem.checkBackground" );
- protected final Insets checkMargins = UIManager.getInsets( "MenuItem.checkMargins" );
+ @Styleable protected Color checkBackground = UIManager.getColor( "MenuItem.checkBackground" );
+ @Styleable protected Insets checkMargins = UIManager.getInsets( "MenuItem.checkMargins" );
- protected final Color underlineSelectionBackground = UIManager.getColor( "MenuItem.underlineSelectionBackground" );
- protected final Color underlineSelectionCheckBackground = UIManager.getColor( "MenuItem.underlineSelectionCheckBackground" );
- protected final Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" );
- protected final int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" );
+ @Styleable protected Color underlineSelectionBackground = UIManager.getColor( "MenuItem.underlineSelectionBackground" );
+ @Styleable protected Color underlineSelectionCheckBackground = UIManager.getColor( "MenuItem.underlineSelectionCheckBackground" );
+ @Styleable protected Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" );
+ @Styleable protected int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" );
- protected final Color selectionBackground = UIManager.getColor( "MenuItem.selectionBackground" );
+ private boolean iconsShared = true;
protected FlatMenuItemRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
Font acceleratorFont, String acceleratorDelimiter )
@@ -98,6 +102,67 @@ public class FlatMenuItemRenderer
this.minimumIconSize = (minimumIconSize != null) ? minimumIconSize : new Dimension( 16, 16 );
}
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ // style icon
+ if( key.startsWith( "icon." ) || key.equals( "selectionForeground" ) ) {
+ if( iconsShared ) {
+ if( checkIcon instanceof FlatCheckBoxMenuItemIcon )
+ checkIcon = FlatStylingSupport.cloneIcon( checkIcon );
+ if( arrowIcon instanceof FlatMenuArrowIcon )
+ arrowIcon = FlatStylingSupport.cloneIcon( arrowIcon );
+ iconsShared = false;
+ }
+
+ if( key.startsWith( "icon." ) ) {
+ String key2 = key.substring( "icon.".length() );
+
+ try {
+ if( checkIcon instanceof FlatCheckBoxMenuItemIcon )
+ return ((FlatCheckBoxMenuItemIcon)checkIcon).applyStyleProperty( key2, value );
+ } catch ( UnknownStyleException ex ) {
+ // ignore
+ }
+
+ try {
+ if( arrowIcon instanceof FlatMenuArrowIcon )
+ return ((FlatMenuArrowIcon)arrowIcon).applyStyleProperty( key2, value );
+ } catch ( UnknownStyleException ex ) {
+ // ignore
+ }
+
+ // keys with prefix "icon." are only for icons
+ throw new UnknownStyleException( key );
+ } else if( key.equals( "selectionForeground" ) ) {
+ // special case: same key is used in icons and in menuitem
+ if( checkIcon instanceof FlatCheckBoxMenuItemIcon )
+ ((FlatCheckBoxMenuItemIcon)checkIcon).applyStyleProperty( key, value );
+ if( arrowIcon instanceof FlatMenuArrowIcon )
+ ((FlatMenuArrowIcon)arrowIcon).applyStyleProperty( key, value );
+
+ // throw exception because the caller should also apply this key
+ throw new UnknownStyleException( key );
+ }
+ }
+
+ return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ public Map> getStyleableInfos() {
+ Map> infos = FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ if( checkIcon instanceof FlatCheckBoxMenuItemIcon )
+ FlatStylingSupport.putAllPrefixKey( infos, "icon.", ((FlatCheckBoxMenuItemIcon)checkIcon).getStyleableInfos() );
+ if( arrowIcon instanceof FlatMenuArrowIcon )
+ FlatStylingSupport.putAllPrefixKey( infos, "icon.", ((FlatMenuArrowIcon)arrowIcon).getStyleableInfos() );
+ infos.remove( "icon.selectionForeground" );
+ return infos;
+ }
+
protected Dimension getPreferredMenuItemSize() {
int width = 0;
int height = 0;
@@ -254,7 +319,7 @@ debug*/
paintBackground( g, underlineSelection ? underlineSelectionBackground : selectionBackground );
if( underlineSelection && isArmedOrSelected( menuItem ) )
paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight );
- paintIcon( g, iconRect, getIconForPainting(), underlineSelection ? underlineSelectionCheckBackground : checkBackground );
+ paintIcon( g, iconRect, getIconForPainting(), underlineSelection ? underlineSelectionCheckBackground : checkBackground, selectionBackground );
paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground );
paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground );
if( !isTopLevelMenu( menuItem ) )
@@ -301,7 +366,7 @@ debug*/
return FlatUIUtils.deriveColor( background, baseColor );
}
- protected void paintIcon( Graphics g, Rectangle iconRect, Icon icon, Color checkBackground ) {
+ protected void paintIcon( Graphics g, Rectangle iconRect, Icon icon, Color checkBackground, Color selectionBackground ) {
// if checkbox/radiobutton menu item is selected and also has a custom icon,
// then use filled icon background to indicate selection (instead of using checkIcon)
if( menuItem.isSelected() && checkIcon != null && icon != checkIcon ) {
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemUI.java
index 78ae0d9e..4d85b14b 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuItemUI.java
@@ -16,13 +16,20 @@
package com.formdev.flatlaf.ui;
+import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
+import java.beans.PropertyChangeListener;
+import java.util.LinkedHashMap;
+import java.util.Map;
import javax.swing.Icon;
import javax.swing.JComponent;
+import javax.swing.JMenuItem;
import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicMenuItemUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuItem}.
@@ -54,13 +61,22 @@ import javax.swing.plaf.basic.BasicMenuItemUI;
*/
public class FlatMenuItemUI
extends BasicMenuItemUI
+ implements StyleableUI
{
private FlatMenuItemRenderer renderer;
+ private Map oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
return new FlatMenuItemUI();
}
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( menuItem ) );
+ }
+
@Override
protected void installDefaults() {
super.installDefaults();
@@ -75,12 +91,72 @@ public class FlatMenuItemUI
super.uninstallDefaults();
renderer = null;
+ oldStyleValues = null;
}
protected FlatMenuItemRenderer createRenderer() {
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
}
+ @Override
+ protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
+ return FlatStylingSupport.createPropertyChangeListener( c, this::applyStyle, super.createPropertyChangeListener( c ) );
+ }
+
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ try {
+ return renderer.applyStyleProperty( key, value );
+ } catch ( UnknownStyleException ex ) {
+ // ignore
+ }
+
+ return applyStyleProperty( this, menuItem, key, value );
+ }
+
+ static Object applyStyleProperty( BasicMenuItemUI ui, JMenuItem menuItem, String key, Object value ) {
+ switch( key ) {
+ // BasicMenuItemUI
+ case "selectionBackground":
+ case "selectionForeground":
+ case "disabledForeground":
+ case "acceleratorForeground":
+ case "acceleratorSelectionForeground":
+ return FlatStylingSupport.applyToField( ui, key, key, value );
+
+ default:
+ return FlatStylingSupport.applyToAnnotatedObjectOrComponent( ui, menuItem, key, value );
+ }
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return getStyleableInfos( renderer );
+ }
+
+ static Map> getStyleableInfos( FlatMenuItemRenderer renderer ) {
+ Map> infos = new LinkedHashMap<>();
+ infos.put( "selectionBackground", Color.class );
+ infos.put( "selectionForeground", Color.class );
+ infos.put( "disabledForeground", Color.class );
+ infos.put( "acceleratorForeground", Color.class );
+ infos.put( "acceleratorSelectionForeground", Color.class );
+ infos.putAll( renderer.getStyleableInfos() );
+ return infos;
+ }
+
@Override
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
return renderer.getPreferredMenuItemSize();
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuUI.java
index 8f80c6c2..edf94112 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuUI.java
@@ -21,16 +21,23 @@ import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Map;
+import java.util.function.Function;
import javax.swing.ButtonModel;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JMenu;
+import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.event.MouseInputListener;
import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.MenuBarUI;
import javax.swing.plaf.basic.BasicMenuUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenu}.
@@ -60,10 +67,10 @@ import javax.swing.plaf.basic.BasicMenuUI;
*
*
* @uiDefault MenuItem.iconTextGap int
- * @uiDefault MenuBar.hoverBackground Color
*
*
*
+ * @uiDefault MenuBar.hoverBackground Color
* @uiDefault MenuBar.underlineSelectionBackground Color
* @uiDefault MenuBar.underlineSelectionColor Color
* @uiDefault MenuBar.underlineSelectionHeight int
@@ -72,14 +79,22 @@ import javax.swing.plaf.basic.BasicMenuUI;
*/
public class FlatMenuUI
extends BasicMenuUI
+ implements StyleableUI
{
- private Color hoverBackground;
private FlatMenuItemRenderer renderer;
+ private Map oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
return new FlatMenuUI();
}
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( menuItem ) );
+ }
+
@Override
protected void installDefaults() {
super.installDefaults();
@@ -88,7 +103,6 @@ public class FlatMenuUI
menuItem.setRolloverEnabled( true );
- hoverBackground = UIManager.getColor( "MenuBar.hoverBackground" );
renderer = createRenderer();
}
@@ -96,8 +110,8 @@ public class FlatMenuUI
protected void uninstallDefaults() {
super.uninstallDefaults();
- hoverBackground = null;
renderer = null;
+ oldStyleValues = null;
}
protected FlatMenuItemRenderer createRenderer() {
@@ -129,6 +143,39 @@ public class FlatMenuUI
};
}
+ @Override
+ protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
+ return FlatStylingSupport.createPropertyChangeListener( c, this::applyStyle, super.createPropertyChangeListener( c ) );
+ }
+
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ try {
+ return renderer.applyStyleProperty( key, value );
+ } catch ( UnknownStyleException ex ) {
+ // ignore
+ }
+
+ return FlatMenuItemUI.applyStyleProperty( this, menuItem, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatMenuItemUI.getStyleableInfos( renderer );
+ }
+
@Override
public Dimension getMinimumSize( JComponent c ) {
// avoid that top-level menus (in menu bar) are made smaller if horizontal space is rare
@@ -153,9 +200,10 @@ public class FlatMenuUI
protected class FlatMenuRenderer
extends FlatMenuItemRenderer
{
- protected final Color menuBarUnderlineSelectionBackground = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionBackground", underlineSelectionBackground );
- protected final Color menuBarUnderlineSelectionColor = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionColor", underlineSelectionColor );
- protected final int menuBarUnderlineSelectionHeight = FlatUIUtils.getUIInt( "MenuBar.underlineSelectionHeight", underlineSelectionHeight );
+ protected Color hoverBackground = UIManager.getColor( "MenuBar.hoverBackground" );
+ protected Color menuBarUnderlineSelectionBackground = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionBackground", underlineSelectionBackground );
+ protected Color menuBarUnderlineSelectionColor = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionColor", underlineSelectionColor );
+ protected int menuBarUnderlineSelectionHeight = FlatUIUtils.getUIInt( "MenuBar.underlineSelectionHeight", underlineSelectionHeight );
protected FlatMenuRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
Font acceleratorFont, String acceleratorDelimiter )
@@ -165,27 +213,39 @@ public class FlatMenuUI
@Override
protected void paintBackground( Graphics g, Color selectionBackground ) {
- if( isUnderlineSelection() && ((JMenu)menuItem).isTopLevelMenu() )
- selectionBackground = menuBarUnderlineSelectionBackground;
+ if( ((JMenu)menuItem).isTopLevelMenu() ) {
+ if( isUnderlineSelection() )
+ selectionBackground = getStyleFromMenuBarUI( ui -> ui.underlineSelectionBackground, menuBarUnderlineSelectionBackground );
- ButtonModel model = menuItem.getModel();
- if( model.isRollover() && !model.isArmed() && !model.isSelected() &&
- model.isEnabled() && ((JMenu)menuItem).isTopLevelMenu() )
- {
- g.setColor( deriveBackground( hoverBackground ) );
- g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
- } else
- super.paintBackground( g, selectionBackground );
+ ButtonModel model = menuItem.getModel();
+ if( model.isRollover() && !model.isArmed() && !model.isSelected() && model.isEnabled() ) {
+ g.setColor( deriveBackground( getStyleFromMenuBarUI( ui -> ui.hoverBackground, hoverBackground ) ) );
+ g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
+ return;
+ }
+ }
+
+ super.paintBackground( g, selectionBackground );
}
@Override
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) {
if( ((JMenu)menuItem).isTopLevelMenu() ) {
- underlineSelectionColor = menuBarUnderlineSelectionColor;
- underlineSelectionHeight = menuBarUnderlineSelectionHeight;
+ underlineSelectionColor = getStyleFromMenuBarUI( ui -> ui.underlineSelectionColor, menuBarUnderlineSelectionColor );
+ underlineSelectionHeight = getStyleFromMenuBarUI( ui -> (ui.underlineSelectionHeight != -1)
+ ? ui.underlineSelectionHeight : null, menuBarUnderlineSelectionHeight );
}
super.paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight );
}
+
+ private T getStyleFromMenuBarUI( Function f, T defaultValue ) {
+ MenuBarUI ui = ((JMenuBar)menuItem.getParent()).getUI();
+ if( !(ui instanceof FlatMenuBarUI) )
+ return defaultValue;
+
+ T value = f.apply( (FlatMenuBarUI) ui );
+ return (value != null) ? value : defaultValue;
+ }
}
}
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPasswordFieldUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPasswordFieldUI.java
index e1f74421..9e90ecde 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPasswordFieldUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPasswordFieldUI.java
@@ -16,6 +16,7 @@
package com.formdev.flatlaf.ui;
+import java.awt.Color;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Shape;
@@ -23,6 +24,7 @@ import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
+import java.util.Map;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.Icon;
@@ -36,6 +38,8 @@ import javax.swing.text.Element;
import javax.swing.text.JTextComponent;
import javax.swing.text.PasswordView;
import javax.swing.text.View;
+import com.formdev.flatlaf.icons.FlatCapsLockIcon;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JPasswordField}.
@@ -75,10 +79,11 @@ import javax.swing.text.View;
public class FlatPasswordFieldUI
extends FlatTextFieldUI
{
- protected boolean showCapsLock;
+ @Styleable protected boolean showCapsLock;
protected Icon capsLockIcon;
private KeyListener capsLockListener;
+ private boolean capsLockIconShared = true;
public static ComponentUI createUI( JComponent c ) {
return new FlatPasswordFieldUI();
@@ -100,6 +105,7 @@ public class FlatPasswordFieldUI
showCapsLock = UIManager.getBoolean( "PasswordField.showCapsLock" );
capsLockIcon = UIManager.getIcon( "PasswordField.capsLockIcon" );
+ capsLockIconShared = true;
}
@Override
@@ -155,6 +161,32 @@ public class FlatPasswordFieldUI
}
}
+ /**
+ * @since 2
+ */
+ @Override
+ protected Object applyStyleProperty( String key, Object value ) {
+ if( key.equals( "capsLockIconColor" ) && capsLockIcon instanceof FlatCapsLockIcon ) {
+ if( capsLockIconShared ) {
+ capsLockIcon = FlatStylingSupport.cloneIcon( capsLockIcon );
+ capsLockIconShared = false;
+ }
+ return ((FlatCapsLockIcon)capsLockIcon).applyStyleProperty( key, value );
+ }
+
+ return super.applyStyleProperty( key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ Map> infos = super.getStyleableInfos( c );
+ infos.put( "capsLockIconColor", Color.class );
+ return infos;
+ }
+
@Override
public View create( Element elem ) {
return new PasswordView( elem );
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupMenuBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupMenuBorder.java
index ce1b314e..8367a97f 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupMenuBorder.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupMenuBorder.java
@@ -16,11 +16,16 @@
package com.formdev.flatlaf.ui;
+import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Insets;
+import java.util.LinkedHashMap;
+import java.util.Map;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
+import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -33,12 +38,44 @@ import com.formdev.flatlaf.util.UIScale;
*/
public class FlatPopupMenuBorder
extends FlatLineBorder
+ implements StyleableBorder
{
+ private Color borderColor;
+
public FlatPopupMenuBorder() {
super( UIManager.getInsets( "PopupMenu.borderInsets" ),
UIManager.getColor( "PopupMenu.borderColor" ) );
}
+ /**
+ * @since 2
+ */
+ @Override
+ public Object applyStyleProperty( String key, Object value ) {
+ Object oldValue;
+ switch( key ) {
+ case "borderInsets": return applyStyleProperty( (Insets) value );
+ case "borderColor": oldValue = getLineColor(); borderColor = (Color) value; return oldValue;
+ }
+ throw new UnknownStyleException( key );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos() {
+ Map> infos = new LinkedHashMap<>();
+ infos.put( "borderInsets", Insets.class );
+ infos.put( "borderColor", Color.class );
+ return infos;
+ }
+
+ @Override
+ public Color getLineColor() {
+ return (borderColor != null) ? borderColor : super.getLineColor();
+ }
+
@Override
public Insets getBorderInsets( Component c, Insets insets ) {
if( c instanceof Container &&
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupMenuSeparatorUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupMenuSeparatorUI.java
index f6181fe4..87bf88ef 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupMenuSeparatorUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupMenuSeparatorUI.java
@@ -39,7 +39,16 @@ public class FlatPopupMenuSeparatorUI
extends FlatSeparatorUI
{
public static ComponentUI createUI( JComponent c ) {
- return FlatUIUtils.createSharedUI( FlatPopupMenuSeparatorUI.class, FlatPopupMenuSeparatorUI::new );
+ return FlatUIUtils.canUseSharedUI( c )
+ ? FlatUIUtils.createSharedUI( FlatPopupMenuSeparatorUI.class, () -> new FlatPopupMenuSeparatorUI( true ) )
+ : new FlatPopupMenuSeparatorUI( false );
+ }
+
+ /**
+ * @since 2
+ */
+ protected FlatPopupMenuSeparatorUI( boolean shared ) {
+ super( shared );
}
@Override
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupMenuUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupMenuUI.java
index 480b0222..7bcea2fa 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupMenuUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupMenuUI.java
@@ -16,9 +16,14 @@
package com.formdev.flatlaf.ui;
+import java.beans.PropertyChangeListener;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.JComponent;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicPopupMenuUI;
+import com.formdev.flatlaf.FlatClientProperties;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JPopupMenu}.
@@ -34,8 +39,68 @@ import javax.swing.plaf.basic.BasicPopupMenuUI;
*/
public class FlatPopupMenuUI
extends BasicPopupMenuUI
+ implements StyleableUI
{
+ private PropertyChangeListener propertyChangeListener;
+ private Map oldStyleValues;
+ private AtomicBoolean borderShared;
+
public static ComponentUI createUI( JComponent c ) {
return new FlatPopupMenuUI();
}
+
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( c ) );
+ }
+
+ @Override
+ public void uninstallUI( JComponent c ) {
+ super.uninstallUI( c );
+
+ oldStyleValues = null;
+ borderShared = null;
+ }
+
+ @Override
+ protected void installListeners() {
+ super.installListeners();
+
+ propertyChangeListener = FlatStylingSupport.createPropertyChangeListener( popupMenu, this::applyStyle, null );
+ popupMenu.addPropertyChangeListener( FlatClientProperties.STYLE, propertyChangeListener );
+ }
+
+ @Override
+ protected void uninstallListeners() {
+ super.uninstallListeners();
+
+ popupMenu.removePropertyChangeListener( FlatClientProperties.STYLE, propertyChangeListener );
+ propertyChangeListener = null;
+ }
+
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ if( borderShared == null )
+ borderShared = new AtomicBoolean( true );
+ return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, popupMenu, borderShared );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this, popupMenu.getBorder() );
+ }
}
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatProgressBarUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatProgressBarUI.java
index dddc87b7..04178b39 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatProgressBarUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatProgressBarUI.java
@@ -25,12 +25,15 @@ import java.awt.Insets;
import java.awt.geom.Area;
import java.awt.geom.RoundRectangle2D;
import java.beans.PropertyChangeListener;
+import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JProgressBar;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicProgressBarUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.UIScale;
@@ -58,17 +61,26 @@ import com.formdev.flatlaf.util.UIScale;
*/
public class FlatProgressBarUI
extends BasicProgressBarUI
+ implements StyleableUI
{
- protected int arc;
- protected Dimension horizontalSize;
- protected Dimension verticalSize;
+ @Styleable protected int arc;
+ @Styleable protected Dimension horizontalSize;
+ @Styleable protected Dimension verticalSize;
private PropertyChangeListener propertyChangeListener;
+ private Map oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
return new FlatProgressBarUI();
}
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( progressBar ) );
+ }
+
@Override
protected void installDefaults() {
super.installDefaults();
@@ -80,6 +92,13 @@ public class FlatProgressBarUI
verticalSize = UIManager.getDimension( "ProgressBar.verticalSize" );
}
+ @Override
+ protected void uninstallDefaults() {
+ super.uninstallDefaults();
+
+ oldStyleValues = null;
+ }
+
@Override
protected void installListeners() {
super.installListeners();
@@ -91,6 +110,12 @@ public class FlatProgressBarUI
progressBar.revalidate();
progressBar.repaint();
break;
+
+ case STYLE:
+ applyStyle( e.getNewValue() );
+ progressBar.revalidate();
+ progressBar.repaint();
+ break;
}
};
progressBar.addPropertyChangeListener( propertyChangeListener );
@@ -104,6 +129,28 @@ public class FlatProgressBarUI
propertyChangeListener = null;
}
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, progressBar, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
+
@Override
public Dimension getPreferredSize( JComponent c ) {
Dimension size = super.getPreferredSize( c );
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRadioButtonMenuItemUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRadioButtonMenuItemUI.java
index 5f65012c..0510ba3b 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRadioButtonMenuItemUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRadioButtonMenuItemUI.java
@@ -18,11 +18,15 @@ package com.formdev.flatlaf.ui;
import java.awt.Dimension;
import java.awt.Graphics;
+import java.beans.PropertyChangeListener;
+import java.util.Map;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JRadioButtonMenuItem}.
@@ -54,13 +58,22 @@ import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI;
*/
public class FlatRadioButtonMenuItemUI
extends BasicRadioButtonMenuItemUI
+ implements StyleableUI
{
private FlatMenuItemRenderer renderer;
+ private Map oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
return new FlatRadioButtonMenuItemUI();
}
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( menuItem ) );
+ }
+
@Override
protected void installDefaults() {
super.installDefaults();
@@ -75,12 +88,46 @@ public class FlatRadioButtonMenuItemUI
super.uninstallDefaults();
renderer = null;
+ oldStyleValues = null;
}
protected FlatMenuItemRenderer createRenderer() {
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
}
+ @Override
+ protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
+ return FlatStylingSupport.createPropertyChangeListener( c, this::applyStyle, super.createPropertyChangeListener( c ) );
+ }
+
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ try {
+ return renderer.applyStyleProperty( key, value );
+ } catch ( UnknownStyleException ex ) {
+ // ignore
+ }
+
+ return FlatMenuItemUI.applyStyleProperty( this, menuItem, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatMenuItemUI.getStyleableInfos( renderer );
+ }
+
@Override
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
return renderer.getPreferredMenuItemSize();
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRadioButtonUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRadioButtonUI.java
index 56450014..642e09a0 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRadioButtonUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRadioButtonUI.java
@@ -23,6 +23,8 @@ import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
+import java.beans.PropertyChangeEvent;
+import java.util.Map;
import java.util.Objects;
import javax.swing.AbstractButton;
import javax.swing.CellRendererPane;
@@ -30,8 +32,13 @@ import javax.swing.JComponent;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicButtonListener;
import javax.swing.plaf.basic.BasicRadioButtonUI;
+import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -56,16 +63,36 @@ import com.formdev.flatlaf.util.UIScale;
*/
public class FlatRadioButtonUI
extends BasicRadioButtonUI
+ implements StyleableUI
{
protected int iconTextGap;
- protected Color disabledText;
+ @Styleable protected Color disabledText;
private Color defaultBackground;
+ private final boolean shared;
+ private boolean iconShared = true;
private boolean defaults_initialized = false;
+ private Map oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
- return FlatUIUtils.createSharedUI( FlatRadioButtonUI.class, FlatRadioButtonUI::new );
+ return FlatUIUtils.canUseSharedUI( c )
+ ? FlatUIUtils.createSharedUI( FlatRadioButtonUI.class, () -> new FlatRadioButtonUI( true ) )
+ : new FlatRadioButtonUI( false );
+ }
+
+ /**
+ * @since 2
+ */
+ protected FlatRadioButtonUI( boolean shared ) {
+ this.shared = shared;
+ }
+
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( (AbstractButton) c, FlatStylingSupport.getStyle( c ) );
}
@Override
@@ -80,6 +107,7 @@ public class FlatRadioButtonUI
defaultBackground = UIManager.getColor( prefix + "background" );
+ iconShared = true;
defaults_initialized = true;
}
@@ -93,10 +121,78 @@ public class FlatRadioButtonUI
protected void uninstallDefaults( AbstractButton b ) {
super.uninstallDefaults( b );
+ oldStyleValues = null;
+
MigLayoutVisualPadding.uninstall( b );
defaults_initialized = false;
}
+ @Override
+ protected BasicButtonListener createButtonListener( AbstractButton b ) {
+ return new FlatRadioButtonListener( b );
+ }
+
+ /**
+ * @since 2
+ */
+ protected void propertyChange( AbstractButton b, PropertyChangeEvent e ) {
+ switch( e.getPropertyName() ) {
+ case FlatClientProperties.STYLE:
+ Object style = e.getNewValue();
+ if( style != null && shared ) {
+ // unshare component UI if necessary
+ // updateUI() invokes applyStyle() from installUI()
+ b.updateUI();
+ } else
+ applyStyle( b, style );
+ b.revalidate();
+ b.repaint();
+ break;
+ }
+ }
+
+ /**
+ * @since 2
+ */
+ protected void applyStyle( AbstractButton b, Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style,
+ (key, value) -> applyStyleProperty( b, key, value ) );
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( AbstractButton b, String key, Object value ) {
+ // style icon
+ if( key.startsWith( "icon." ) ) {
+ if( !(icon instanceof FlatCheckBoxIcon) )
+ return new UnknownStyleException( key );
+
+ if( iconShared ) {
+ icon = FlatStylingSupport.cloneIcon( icon );
+ iconShared = false;
+ }
+
+ key = key.substring( "icon.".length() );
+ return ((FlatCheckBoxIcon)icon).applyStyleProperty( key, value );
+ }
+
+ return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, b, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ Map> infos = FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ if( icon instanceof FlatCheckBoxIcon ) {
+ for( Map.Entry> e : ((FlatCheckBoxIcon)icon).getStyleableInfos().entrySet() )
+ infos.put( "icon.".concat( e.getKey() ), e.getValue() );
+ }
+ return infos;
+ }
+
private static Insets tempInsets = new Insets( 0, 0, 0, 0 );
@Override
@@ -182,4 +278,26 @@ public class FlatRadioButtonUI
? UIScale.scale( ((FlatCheckBoxIcon)getDefaultIcon()).focusWidth )
: 0;
}
+
+ //---- class FlatRadioButtonListener --------------------------------------
+
+ /**
+ * @since 2
+ */
+ protected class FlatRadioButtonListener
+ extends BasicButtonListener
+ {
+ private final AbstractButton b;
+
+ protected FlatRadioButtonListener( AbstractButton b ) {
+ super( b );
+ this.b = b;
+ }
+
+ @Override
+ public void propertyChange( PropertyChangeEvent e ) {
+ super.propertyChange( e );
+ FlatRadioButtonUI.this.propertyChange( b, e );
+ }
+ }
}
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRoundBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRoundBorder.java
index 3703be9c..f9cce14d 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRoundBorder.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRoundBorder.java
@@ -18,6 +18,7 @@ package com.formdev.flatlaf.ui;
import java.awt.Component;
import javax.swing.UIManager;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
/**
* Border for various components (e.g. {@link javax.swing.JComboBox}).
@@ -29,7 +30,7 @@ import javax.swing.UIManager;
public class FlatRoundBorder
extends FlatBorder
{
- protected final int arc = UIManager.getInt( "Component.arc" );
+ @Styleable protected int arc = UIManager.getInt( "Component.arc" );
@Override
protected int getArc( Component c ) {
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollBarUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollBarUI.java
index bdc1d925..3c83968c 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollBarUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollBarUI.java
@@ -24,6 +24,8 @@ import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeListener;
+import java.util.LinkedHashMap;
+import java.util.Map;
import java.util.Objects;
import javax.swing.InputMap;
import javax.swing.JButton;
@@ -35,6 +37,8 @@ import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicScrollBarUI;
import com.formdev.flatlaf.FlatClientProperties;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -74,33 +78,46 @@ import com.formdev.flatlaf.util.UIScale;
*/
public class FlatScrollBarUI
extends BasicScrollBarUI
+ implements StyleableUI
{
- protected Insets trackInsets;
- protected Insets thumbInsets;
- protected int trackArc;
- protected int thumbArc;
- protected Color hoverTrackColor;
- protected Color hoverThumbColor;
- protected boolean hoverThumbWithTrack;
- protected Color pressedTrackColor;
- protected Color pressedThumbColor;
- protected boolean pressedThumbWithTrack;
+ // overrides BasicScrollBarUI.supportsAbsolutePositioning (which is private)
+ @Styleable protected boolean allowsAbsolutePositioning;
- protected boolean showButtons;
- protected String arrowType;
- protected Color buttonArrowColor;
- protected Color buttonDisabledArrowColor;
- protected Color hoverButtonBackground;
- protected Color pressedButtonBackground;
+ @Styleable protected Insets trackInsets;
+ @Styleable protected Insets thumbInsets;
+ @Styleable protected int trackArc;
+ @Styleable protected int thumbArc;
+ @Styleable protected Color hoverTrackColor;
+ @Styleable protected Color hoverThumbColor;
+ @Styleable protected boolean hoverThumbWithTrack;
+ @Styleable protected Color pressedTrackColor;
+ @Styleable protected Color pressedThumbColor;
+ @Styleable protected boolean pressedThumbWithTrack;
+
+ @Styleable protected boolean showButtons;
+ @Styleable protected String arrowType;
+ @Styleable protected Color buttonArrowColor;
+ @Styleable protected Color buttonDisabledArrowColor;
+ @Styleable protected Color hoverButtonBackground;
+ @Styleable protected Color pressedButtonBackground;
private MouseAdapter hoverListener;
protected boolean hoverTrack;
protected boolean hoverThumb;
+ private Map oldStyleValues;
+
public static ComponentUI createUI( JComponent c ) {
return new FlatScrollBarUI();
}
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( c ) );
+ }
+
@Override
protected void installListeners() {
super.installListeners();
@@ -123,6 +140,8 @@ public class FlatScrollBarUI
protected void installDefaults() {
super.installDefaults();
+ allowsAbsolutePositioning = super.getSupportsAbsolutePositioning();
+
trackInsets = UIManager.getInsets( "ScrollBar.trackInsets" );
thumbInsets = UIManager.getInsets( "ScrollBar.thumbInsets" );
trackArc = UIManager.getInt( "ScrollBar.trackArc" );
@@ -163,6 +182,8 @@ public class FlatScrollBarUI
buttonDisabledArrowColor = null;
hoverButtonBackground = null;
pressedButtonBackground = null;
+
+ oldStyleValues = null;
}
@Override
@@ -177,6 +198,12 @@ public class FlatScrollBarUI
scrollbar.repaint();
break;
+ case FlatClientProperties.STYLE:
+ applyStyle( e.getNewValue() );
+ scrollbar.revalidate();
+ scrollbar.repaint();
+ break;
+
case "componentOrientation":
// this is missing in BasicScrollBarUI.Handler.propertyChange()
InputMap inputMap = (InputMap) UIManager.get( "ScrollBar.ancestorInputMap" );
@@ -193,6 +220,50 @@ public class FlatScrollBarUI
};
}
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+
+ if( incrButton instanceof FlatScrollBarButton )
+ ((FlatScrollBarButton)incrButton).updateStyle();
+ if( decrButton instanceof FlatScrollBarButton )
+ ((FlatScrollBarButton)decrButton).updateStyle();
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ Object oldValue;
+ switch( key ) {
+ // BasicScrollBarUI
+ case "track": oldValue = trackColor; trackColor = (Color) value; return oldValue;
+ case "thumb": oldValue = thumbColor; thumbColor = (Color) value; return oldValue;
+ case "width": oldValue = scrollBarWidth; scrollBarWidth = (int) value; return oldValue;
+ case "minimumThumbSize": oldValue = minimumThumbSize; minimumThumbSize = (Dimension) value; return oldValue;
+ case "maximumThumbSize": oldValue = maximumThumbSize; maximumThumbSize = (Dimension) value; return oldValue;
+ }
+
+ return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, scrollbar, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ Map> infos = new LinkedHashMap<>();
+ infos.put( "track", Color.class );
+ infos.put( "thumb", Color.class );
+ infos.put( "width", int.class );
+ infos.put( "minimumThumbSize", Dimension.class );
+ infos.put( "maximumThumbSize", Dimension.class );
+ FlatStylingSupport.collectAnnotatedStyleableInfos( this, infos );
+ return infos;
+ }
+
@Override
public Dimension getPreferredSize( JComponent c ) {
return UIScale.scale( super.getPreferredSize( c ) );
@@ -295,6 +366,11 @@ public class FlatScrollBarUI
return UIScale.scale( FlatUIUtils.addInsets( super.getMaximumThumbSize(), thumbInsets ) );
}
+ @Override
+ public boolean getSupportsAbsolutePositioning() {
+ return allowsAbsolutePositioning;
+ }
+
//---- class ScrollBarHoverListener ---------------------------------------
// using static field to disabling hover for other scroll bars
@@ -368,6 +444,11 @@ public class FlatScrollBarUI
setRequestFocusEnabled( false );
}
+ protected void updateStyle() {
+ updateStyle( arrowType, buttonArrowColor, buttonDisabledArrowColor,
+ null, hoverButtonBackground, null, pressedButtonBackground );
+ }
+
@Override
protected Color deriveBackground( Color background ) {
return FlatUIUtils.deriveColor( background, scrollbar.getBackground() );
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollPaneUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollPaneUI.java
index 0cc8a740..1b46ef38 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollPaneUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollPaneUI.java
@@ -29,6 +29,8 @@ import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
@@ -46,6 +48,7 @@ import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicScrollPaneUI;
import com.formdev.flatlaf.FlatClientProperties;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JScrollPane}.
@@ -66,9 +69,13 @@ import com.formdev.flatlaf.FlatClientProperties;
*/
public class FlatScrollPaneUI
extends BasicScrollPaneUI
+ implements StyleableUI
{
private Handler handler;
+ private Map oldStyleValues;
+ private AtomicBoolean borderShared;
+
public static ComponentUI createUI( JComponent c ) {
return new FlatScrollPaneUI();
}
@@ -80,6 +87,8 @@ public class FlatScrollPaneUI
int focusWidth = UIManager.getInt( "Component.focusWidth" );
LookAndFeel.installProperty( c, "opaque", focusWidth == 0 );
+ applyStyle( FlatStylingSupport.getStyle( c ) );
+
MigLayoutVisualPadding.install( scrollpane );
}
@@ -88,6 +97,9 @@ public class FlatScrollPaneUI
MigLayoutVisualPadding.uninstall( scrollpane );
super.uninstallUI( c );
+
+ oldStyleValues = null;
+ borderShared = null;
}
@Override
@@ -272,7 +284,13 @@ public class FlatScrollPaneUI
((JButton)corner).setBorder( BorderFactory.createEmptyBorder() );
((JButton)corner).setFocusable( false );
}
- break;
+ break;
+
+ case FlatClientProperties.STYLE:
+ applyStyle( e.getNewValue() );
+ scrollpane.revalidate();
+ scrollpane.repaint();
+ break;
}
};
}
@@ -283,6 +301,35 @@ public class FlatScrollPaneUI
return handler;
}
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ if( key.equals( "focusWidth" ) ) {
+ int focusWidth = (value instanceof Integer) ? (int) value : UIManager.getInt( "Component.focusWidth" );
+ LookAndFeel.installProperty( scrollpane, "opaque", focusWidth == 0 );
+ }
+
+ if( borderShared == null )
+ borderShared = new AtomicBoolean( true );
+ return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, scrollpane, borderShared );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this, scrollpane.getBorder() );
+ }
+
@Override
protected void updateViewport( PropertyChangeEvent e ) {
super.updateViewport( e );
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSeparatorUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSeparatorUI.java
index 7ace822b..8310e59e 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSeparatorUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSeparatorUI.java
@@ -21,11 +21,16 @@ import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
+import java.beans.PropertyChangeListener;
+import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JSeparator;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicSeparatorUI;
+import com.formdev.flatlaf.FlatClientProperties;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JSeparator}.
@@ -45,15 +50,39 @@ import javax.swing.plaf.basic.BasicSeparatorUI;
*/
public class FlatSeparatorUI
extends BasicSeparatorUI
+ implements StyleableUI
{
- protected int height;
- protected int stripeWidth;
- protected int stripeIndent;
+ @Styleable protected int height;
+ @Styleable protected int stripeWidth;
+ @Styleable protected int stripeIndent;
+ private final boolean shared;
private boolean defaults_initialized = false;
+ private PropertyChangeListener propertyChangeListener;
+ private Map oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
- return FlatUIUtils.createSharedUI( FlatSeparatorUI.class, FlatSeparatorUI::new );
+ return FlatUIUtils.canUseSharedUI( c )
+ ? FlatUIUtils.createSharedUI( FlatSeparatorUI.class, () -> new FlatSeparatorUI( true ) )
+ : new FlatSeparatorUI( false );
+ }
+
+ /**
+ * @since 2
+ */
+ protected FlatSeparatorUI( boolean shared ) {
+ this.shared = shared;
+ }
+
+ protected String getPropertyPrefix() {
+ return "Separator";
+ }
+
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( (JSeparator) c, FlatStylingSupport.getStyle( c ) );
}
@Override
@@ -73,11 +102,60 @@ public class FlatSeparatorUI
@Override
protected void uninstallDefaults( JSeparator s ) {
super.uninstallDefaults( s );
+
defaults_initialized = false;
+ oldStyleValues = null;
}
- protected String getPropertyPrefix() {
- return "Separator";
+ @Override
+ protected void installListeners( JSeparator s ) {
+ super.installListeners( s );
+
+ propertyChangeListener = FlatStylingSupport.createPropertyChangeListener(
+ s, style -> applyStyle( s, this, style ), null );
+ s.addPropertyChangeListener( FlatClientProperties.STYLE, propertyChangeListener );
+ }
+
+ @Override
+ protected void uninstallListeners( JSeparator s ) {
+ super.uninstallListeners( s );
+
+ s.removePropertyChangeListener( FlatClientProperties.STYLE, propertyChangeListener );
+ propertyChangeListener = null;
+ }
+
+ private static void applyStyle( JSeparator s, FlatSeparatorUI ui, Object style ) {
+ if( style != null && ui.shared ) {
+ // unshare component UI if necessary
+ // updateUI() invokes applyStyle() from installUI()
+ s.updateUI();
+ } else
+ ui.applyStyle( s, style );
+ s.revalidate();
+ s.repaint();
+ }
+
+ /**
+ * @since 2
+ */
+ protected void applyStyle( JSeparator s, Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style,
+ (key, value) -> applyStyleProperty( s, key, value ) );
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( JSeparator s, String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, s, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
}
@Override
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSliderUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSliderUI.java
index 5a1b7352..d72fa7b1 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSliderUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSliderUI.java
@@ -29,6 +29,8 @@ import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.RoundRectangle2D;
+import java.beans.PropertyChangeListener;
+import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JSlider;
import javax.swing.LookAndFeel;
@@ -36,6 +38,9 @@ import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicSliderUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
+import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.UIScale;
@@ -75,23 +80,25 @@ import com.formdev.flatlaf.util.UIScale;
*/
public class FlatSliderUI
extends BasicSliderUI
+ implements StyleableUI
{
- protected int trackWidth;
- protected Dimension thumbSize;
- protected int focusWidth;
+ @Styleable protected int trackWidth;
+ @Styleable protected Dimension thumbSize;
+ @Styleable protected int focusWidth;
- protected Color trackValueColor;
- protected Color trackColor;
- protected Color thumbColor;
- protected Color thumbBorderColor;
+ @Styleable protected Color trackValueColor;
+ @Styleable protected Color trackColor;
+ @Styleable protected Color thumbColor;
+ @Styleable protected Color thumbBorderColor;
protected Color focusBaseColor;
- protected Color focusedColor;
- protected Color focusedThumbBorderColor;
- protected Color hoverThumbColor;
- protected Color pressedThumbColor;
- protected Color disabledTrackColor;
- protected Color disabledThumbColor;
- protected Color disabledThumbBorderColor;
+ @Styleable protected Color focusedColor;
+ @Styleable protected Color focusedThumbBorderColor;
+ @Styleable protected Color hoverThumbColor;
+ @Styleable protected Color pressedThumbColor;
+ @Styleable protected Color disabledTrackColor;
+ @Styleable protected Color disabledThumbColor;
+ @Styleable protected Color disabledThumbBorderColor;
+ @Styleable protected Color tickColor;
private Color defaultBackground;
private Color defaultForeground;
@@ -100,6 +107,7 @@ public class FlatSliderUI
protected boolean thumbPressed;
private Object[] oldRenderingHints;
+ private Map oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
return new FlatSliderUI();
@@ -109,6 +117,13 @@ public class FlatSliderUI
super( null );
}
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( slider ) );
+ }
+
@Override
protected void installDefaults( JSlider slider ) {
super.installDefaults( slider );
@@ -136,6 +151,7 @@ public class FlatSliderUI
disabledTrackColor = UIManager.getColor( "Slider.disabledTrackColor" );
disabledThumbColor = UIManager.getColor( "Slider.disabledThumbColor" );
disabledThumbBorderColor = FlatUIUtils.getUIColor( "Slider.disabledThumbBorderColor", "Component.disabledBorderColor" );
+ tickColor = FlatUIUtils.getUIColor( "Slider.tickColor", Color.BLACK ); // see BasicSliderUI.paintTicks()
defaultBackground = UIManager.getColor( "Slider.background" );
defaultForeground = UIManager.getColor( "Slider.foreground" );
@@ -157,9 +173,12 @@ public class FlatSliderUI
disabledTrackColor = null;
disabledThumbColor = null;
disabledThumbBorderColor = null;
+ tickColor = null;
defaultBackground = null;
defaultForeground = null;
+
+ oldStyleValues = null;
}
@Override
@@ -167,6 +186,34 @@ public class FlatSliderUI
return new FlatTrackListener();
}
+ @Override
+ protected PropertyChangeListener createPropertyChangeListener( JSlider slider ) {
+ return FlatStylingSupport.createPropertyChangeListener( slider, this::applyStyle,
+ super.createPropertyChangeListener( slider ) );
+ }
+
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, slider, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
+
@Override
public int getBaseline( JComponent c, int width, int height ) {
if( c == null )
@@ -326,6 +373,19 @@ debug*/
((Graphics2D)g).fill( track );
}
+ @Override
+ public void paintTicks( Graphics g ) {
+ // because BasicSliderUI.paintTicks() always uses
+ // g.setColor( UIManager.getColor("Slider.tickColor") )
+ // we override this method and use our tickColor field to allow styling
+ super.paintTicks( new Graphics2DProxy( (Graphics2D) g ) {
+ @Override
+ public void setColor( Color c ) {
+ super.setColor( tickColor );
+ }
+ } );
+ }
+
@Override
public void paintThumb( Graphics g ) {
Color thumbColor = getThumbColor();
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSpinnerUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSpinnerUI.java
index fdfdf657..1092434f 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSpinnerUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSpinnerUI.java
@@ -33,6 +33,8 @@ import java.awt.event.FocusListener;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.JComponent;
import javax.swing.JSpinner;
import javax.swing.JTextField;
@@ -43,6 +45,8 @@ import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicSpinnerUI;
import com.formdev.flatlaf.FlatClientProperties;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JSpinner}.
@@ -79,29 +83,40 @@ import com.formdev.flatlaf.FlatClientProperties;
*/
public class FlatSpinnerUI
extends BasicSpinnerUI
+ implements StyleableUI
{
private Handler handler;
- protected int minimumWidth;
- protected String buttonStyle;
- protected String arrowType;
+ @Styleable protected int minimumWidth;
+ @Styleable protected String buttonStyle;
+ @Styleable protected String arrowType;
protected boolean isIntelliJTheme;
- protected Color borderColor;
- protected Color disabledBorderColor;
- protected Color disabledBackground;
- protected Color disabledForeground;
- protected Color focusedBackground;
- protected Color buttonBackground;
- protected Color buttonArrowColor;
- protected Color buttonDisabledArrowColor;
- protected Color buttonHoverArrowColor;
- protected Color buttonPressedArrowColor;
- protected Insets padding;
+ @Styleable protected Color borderColor;
+ @Styleable protected Color disabledBorderColor;
+ @Styleable protected Color disabledBackground;
+ @Styleable protected Color disabledForeground;
+ @Styleable protected Color focusedBackground;
+ @Styleable protected Color buttonBackground;
+ @Styleable protected Color buttonArrowColor;
+ @Styleable protected Color buttonDisabledArrowColor;
+ @Styleable protected Color buttonHoverArrowColor;
+ @Styleable protected Color buttonPressedArrowColor;
+ @Styleable protected Insets padding;
+
+ private Map oldStyleValues;
+ private AtomicBoolean borderShared;
public static ComponentUI createUI( JComponent c ) {
return new FlatSpinnerUI();
}
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( spinner ) );
+ }
+
@Override
protected void installDefaults() {
super.installDefaults();
@@ -143,6 +158,9 @@ public class FlatSpinnerUI
buttonPressedArrowColor = null;
padding = null;
+ oldStyleValues = null;
+ borderShared = null;
+
MigLayoutVisualPadding.uninstall( spinner );
}
@@ -172,6 +190,32 @@ public class FlatSpinnerUI
return handler;
}
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+ updateEditorPadding();
+ updateArrowButtonsStyle();
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ if( borderShared == null )
+ borderShared = new AtomicBoolean( true );
+ return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, spinner, borderShared );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this, spinner.getBorder() );
+ }
+
@Override
protected JComponent createEditor() {
JComponent editor = super.createEditor();
@@ -297,6 +341,15 @@ public class FlatSpinnerUI
return button;
}
+ private void updateArrowButtonsStyle() {
+ for( Component c : spinner.getComponents() ) {
+ if( c instanceof FlatArrowButton ) {
+ ((FlatArrowButton)c).updateStyle( arrowType, buttonArrowColor,
+ buttonDisabledArrowColor, buttonHoverArrowColor, null, buttonPressedArrowColor, null );
+ }
+ }
+ }
+
@Override
public void update( Graphics g, JComponent c ) {
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
@@ -483,6 +536,12 @@ public class FlatSpinnerUI
case FlatClientProperties.MINIMUM_WIDTH:
spinner.revalidate();
break;
+
+ case FlatClientProperties.STYLE:
+ applyStyle( e.getNewValue() );
+ spinner.revalidate();
+ spinner.repaint();
+ break;
}
}
}
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSplitPaneUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSplitPaneUI.java
index 16ec4eba..1eb62027 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSplitPaneUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSplitPaneUI.java
@@ -23,6 +23,8 @@ import java.awt.Graphics;
import java.awt.Insets;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Map;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JSplitPane;
@@ -32,6 +34,10 @@ import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicSplitPaneDivider;
import javax.swing.plaf.basic.BasicSplitPaneUI;
+import com.formdev.flatlaf.FlatClientProperties;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -66,16 +72,27 @@ import com.formdev.flatlaf.util.UIScale;
*/
public class FlatSplitPaneUI
extends BasicSplitPaneUI
+ implements StyleableUI
{
- protected String arrowType;
- protected Color oneTouchArrowColor;
- protected Color oneTouchHoverArrowColor;
- protected Color oneTouchPressedArrowColor;
+ @Styleable protected String arrowType;
+ @Styleable protected Color oneTouchArrowColor;
+ @Styleable protected Color oneTouchHoverArrowColor;
+ @Styleable protected Color oneTouchPressedArrowColor;
+
+ private PropertyChangeListener propertyChangeListener;
+ private Map oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
return new FlatSplitPaneUI();
}
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( splitPane ) );
+ }
+
@Override
protected void installDefaults() {
arrowType = UIManager.getString( "Component.arrowType" );
@@ -96,6 +113,24 @@ public class FlatSplitPaneUI
oneTouchArrowColor = null;
oneTouchHoverArrowColor = null;
oneTouchPressedArrowColor = null;
+
+ oldStyleValues = null;
+ }
+
+ @Override
+ protected void installListeners() {
+ super.installListeners();
+
+ propertyChangeListener = FlatStylingSupport.createPropertyChangeListener( splitPane, this::applyStyle, null );
+ splitPane.addPropertyChangeListener( FlatClientProperties.STYLE, propertyChangeListener );
+ }
+
+ @Override
+ protected void uninstallListeners() {
+ super.uninstallListeners();
+
+ splitPane.removePropertyChangeListener( FlatClientProperties.STYLE, propertyChangeListener );
+ propertyChangeListener = null;
}
@Override
@@ -103,16 +138,50 @@ public class FlatSplitPaneUI
return new FlatSplitPaneDivider( this );
}
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+
+ if( divider instanceof FlatSplitPaneDivider )
+ ((FlatSplitPaneDivider)divider).updateStyle();
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ try {
+ if( divider instanceof FlatSplitPaneDivider )
+ return ((FlatSplitPaneDivider)divider).applyStyleProperty( key, value );
+ } catch( UnknownStyleException ex ) {
+ // ignore
+ }
+ return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, splitPane, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ Map> infos = FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ if( divider instanceof FlatSplitPaneDivider )
+ infos.putAll( ((FlatSplitPaneDivider)divider).getStyleableInfos() );
+ return infos;
+ }
+
//---- class FlatSplitPaneDivider -----------------------------------------
protected class FlatSplitPaneDivider
extends BasicSplitPaneDivider
{
- protected final String style = UIManager.getString( "SplitPaneDivider.style" );
- protected final Color gripColor = UIManager.getColor( "SplitPaneDivider.gripColor" );
- protected final int gripDotCount = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotCount", 3 );
- protected final int gripDotSize = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotSize", 3 );
- protected final int gripGap = FlatUIUtils.getUIInt( "SplitPaneDivider.gripGap", 2 );
+ @Styleable protected String style = UIManager.getString( "SplitPaneDivider.style" );
+ @Styleable protected Color gripColor = UIManager.getColor( "SplitPaneDivider.gripColor" );
+ @Styleable protected int gripDotCount = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotCount", 3 );
+ @Styleable protected int gripDotSize = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotSize", 3 );
+ @Styleable protected int gripGap = FlatUIUtils.getUIInt( "SplitPaneDivider.gripGap", 2 );
protected FlatSplitPaneDivider( BasicSplitPaneUI ui ) {
super( ui );
@@ -120,6 +189,27 @@ public class FlatSplitPaneUI
setLayout( new FlatDividerLayout() );
}
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ public Map> getStyleableInfos() {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
+
+ void updateStyle() {
+ if( leftButton instanceof FlatOneTouchButton )
+ ((FlatOneTouchButton)leftButton).updateStyle();
+ if( rightButton instanceof FlatOneTouchButton )
+ ((FlatOneTouchButton)rightButton).updateStyle();
+ }
+
@Override
public void setDividerSize( int newSize ) {
super.setDividerSize( UIScale.scale( newSize ) );
@@ -200,6 +290,11 @@ public class FlatSplitPaneUI
this.left = left;
}
+ protected void updateStyle() {
+ updateStyle( arrowType, oneTouchArrowColor, null,
+ oneTouchHoverArrowColor, null, oneTouchPressedArrowColor, null );
+ }
+
@Override
public int getDirection() {
return (orientation == JSplitPane.VERTICAL_SPLIT)
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatStylingSupport.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatStylingSupport.java
new file mode 100644
index 00000000..d66657e3
--- /dev/null
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatStylingSupport.java
@@ -0,0 +1,532 @@
+/*
+ * Copyright 2021 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.ui;
+
+import java.beans.PropertyChangeListener;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import javax.swing.Icon;
+import javax.swing.JComponent;
+import javax.swing.UIManager;
+import javax.swing.border.Border;
+import com.formdev.flatlaf.FlatClientProperties;
+import com.formdev.flatlaf.FlatLaf;
+import com.formdev.flatlaf.util.StringUtils;
+import com.formdev.flatlaf.util.SystemInfo;
+
+/**
+ * Support for styling components in CSS syntax.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public class FlatStylingSupport
+{
+ /**
+ * Indicates that a field is intended to be used by FlatLaf styling support.
+ *
+ * Do not rename fields annotated with this annotation.
+ */
+ @Target(ElementType.FIELD)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface Styleable {
+ boolean dot() default false;
+ Class> type() default Void.class;
+ }
+
+ public interface StyleableUI {
+ Map> getStyleableInfos( JComponent c );
+ }
+
+ public interface StyleableBorder {
+ Object applyStyleProperty( String key, Object value );
+ Map> getStyleableInfos();
+ }
+
+ /**
+ * Parses styles in CSS syntax ("key1: value1; key2: value2; ..."),
+ * converts the value strings into binary and invokes the given function
+ * to apply the properties.
+ *
+ * @param oldStyleValues map of old values modified by the previous invocation, or {@code null}
+ * @param style the style in CSS syntax as string, or a Map, or {@code null}
+ * @param applyProperty function that is invoked to apply the properties;
+ * first parameter is the key, second the binary value;
+ * the function must return the old value
+ * @return map of old values modified by the given style, or {@code null}
+ * @throws UnknownStyleException on unknown style keys
+ * @throws IllegalArgumentException on syntax errors
+ * @throws ClassCastException if value type does not fit to expected type
+ */
+ public static Map parseAndApply( Map oldStyleValues,
+ Object style, BiFunction applyProperty )
+ throws UnknownStyleException, IllegalArgumentException
+ {
+ // restore previous values
+ if( oldStyleValues != null ) {
+ for( Map.Entry e : oldStyleValues.entrySet() )
+ applyProperty.apply( e.getKey(), e.getValue() );
+ }
+
+ // ignore empty style
+ if( style == null )
+ return null;
+
+ if( style instanceof String ) {
+ // handle style in CSS syntax
+ String str = (String) style;
+ if( str.trim().isEmpty() )
+ return null;
+
+ return applyStyle( parse( str ), applyProperty );
+ } else if( style instanceof Map ) {
+ // handle style of type Map
+ @SuppressWarnings( "unchecked" )
+ Map map = (Map) style;
+ return applyStyle( map, applyProperty );
+ } else
+ return null;
+ }
+
+ private static Map applyStyle( Map style,
+ BiFunction applyProperty )
+ {
+ if( style.isEmpty() )
+ return null;
+
+ Map oldValues = new HashMap<>();
+ for( Map.Entry e : style.entrySet() ) {
+ String key = e.getKey();
+ Object newValue = e.getValue();
+
+ // handle key prefix
+ if( key.startsWith( "[" ) ) {
+ if( (SystemInfo.isWindows && key.startsWith( "[win]" )) ||
+ (SystemInfo.isMacOS && key.startsWith( "[mac]" )) ||
+ (SystemInfo.isLinux && key.startsWith( "[linux]" )) ||
+ (key.startsWith( "[light]" ) && !FlatLaf.isLafDark()) ||
+ (key.startsWith( "[dark]" ) && FlatLaf.isLafDark()) )
+ {
+ // prefix is known and enabled --> remove prefix
+ key = key.substring( key.indexOf( ']' ) + 1 );
+ } else
+ continue;
+ }
+
+ Object oldValue = applyProperty.apply( key, newValue );
+ oldValues.put( key, oldValue );
+ }
+ return oldValues;
+ }
+
+ /**
+ * Parses styles in CSS syntax ("key1: value1; key2: value2; ..."),
+ * converts the value strings into binary and returns all key/value pairs as map.
+ *
+ * @param style the style in CSS syntax, or {@code null}
+ * @return map of parsed styles, or {@code null}
+ * @throws IllegalArgumentException on syntax errors
+ */
+ public static Map parse( String style )
+ throws IllegalArgumentException
+ {
+ if( style == null || style.trim().isEmpty() )
+ return null;
+
+ Map map = null;
+
+ // split style into parts and process them
+ for( String part : StringUtils.split( style, ';' ) ) {
+ // ignore empty parts
+ part = part.trim();
+ if( part.isEmpty() )
+ continue;
+
+ // find separator colon
+ int sepIndex = part.indexOf( ':' );
+ if( sepIndex < 0 )
+ throw new IllegalArgumentException( "missing colon in '" + part + "'" );
+
+ // split into key and value
+ String key = part.substring( 0, sepIndex ).trim();
+ String value = part.substring( sepIndex + 1 ).trim();
+ if( key.isEmpty() )
+ throw new IllegalArgumentException( "missing key in '" + part + "'" );
+ if( value.isEmpty() )
+ throw new IllegalArgumentException( "missing value in '" + part + "'" );
+
+ // parse value string and convert it into binary value
+ if( map == null )
+ map = new LinkedHashMap<>();
+ map.put( key, parseValue( key, value ) );
+ }
+
+ return map;
+ }
+
+ private static Object parseValue( String key, String value ) {
+ // simple reference
+ if( value.startsWith( "$" ) )
+ return UIManager.get( value.substring( 1 ) );
+
+ // remove key prefix for correct value type detection
+ // (e.g. "[light]padding" would not parse to Insets)
+ if( key.startsWith( "[" ) )
+ key = key.substring( key.indexOf( ']' ) + 1 );
+
+ // parse string
+ return FlatLaf.parseDefaultsValue( key, value, null );
+ }
+
+ /**
+ * Applies the given value to an annotated field of the given object.
+ * The field must be annotated with {@link Styleable}.
+ *
+ * @param obj the object
+ * @param key the name of the field
+ * @param value the new value
+ * @return the old value of the field
+ * @throws UnknownStyleException if object does not have a annotated field with given name
+ * @throws IllegalArgumentException if value type does not fit to expected type
+ */
+ public static Object applyToAnnotatedObject( Object obj, String key, Object value )
+ throws UnknownStyleException, IllegalArgumentException
+ {
+ String fieldName = key;
+ int dotIndex = key.indexOf( '.' );
+ if( dotIndex >= 0 ) {
+ // remove first dot in key and change subsequent character to uppercase
+ fieldName = key.substring( 0, dotIndex )
+ + Character.toUpperCase( key.charAt( dotIndex + 1 ) )
+ + key.substring( dotIndex + 2 );
+ }
+
+ return applyToField( obj, fieldName, key, value, field -> {
+ Styleable styleable = field.getAnnotation( Styleable.class );
+ return styleable != null && styleable.dot() == (dotIndex >= 0);
+ } );
+ }
+
+ /**
+ * Applies the given value to a field of the given object.
+ *
+ * @param obj the object
+ * @param fieldName the name of the field
+ * @param key the key (only used for error reporting)
+ * @param value the new value
+ * @return the old value of the field
+ * @throws UnknownStyleException if object does not have a field with given name
+ * @throws IllegalArgumentException if value type does not fit to expected type
+ */
+ static Object applyToField( Object obj, String fieldName, String key, Object value )
+ throws UnknownStyleException, IllegalArgumentException
+ {
+ return applyToField( obj, fieldName, key, value, null );
+ }
+
+ private static Object applyToField( Object obj, String fieldName, String key, Object value, Predicate predicate )
+ throws UnknownStyleException, IllegalArgumentException
+ {
+ Class> cls = obj.getClass();
+
+ for(;;) {
+ try {
+ Field f = cls.getDeclaredField( fieldName );
+ if( predicate == null || predicate.test( f ) ) {
+ if( !isValidField( f ) )
+ throw new IllegalArgumentException( "field '" + cls.getName() + "." + fieldName + "' is final or static" );
+
+ try {
+ // necessary to access protected fields in other packages
+ f.setAccessible( true );
+
+ // get old value and set new value
+ Object oldValue = f.get( obj );
+ f.set( obj, value );
+ return oldValue;
+ } catch( IllegalAccessException ex ) {
+ throw new IllegalArgumentException( "failed to access field '" + cls.getName() + "." + fieldName + "'" );
+ }
+ }
+ } catch( NoSuchFieldException ex ) {
+ // field not found in class --> try superclass
+ }
+
+ cls = cls.getSuperclass();
+ if( cls == null )
+ throw new UnknownStyleException( key );
+
+ if( predicate != null ) {
+ String superclassName = cls.getName();
+ if( superclassName.startsWith( "java." ) || superclassName.startsWith( "javax." ) )
+ throw new UnknownStyleException( key );
+ }
+ }
+ }
+
+ private static boolean isValidField( Field f ) {
+ int modifiers = f.getModifiers();
+ return (modifiers & (Modifier.FINAL|Modifier.STATIC)) == 0 && !f.isSynthetic();
+ }
+
+ /**
+ * Applies the given value to a property of the given object.
+ * Works only for properties that have public getter and setter methods.
+ * First the property getter is invoked to get the old value,
+ * then the property setter is invoked to set the new value.
+ *
+ * @param obj the object
+ * @param name the name of the property
+ * @param value the new value
+ * @return the old value of the property
+ * @throws UnknownStyleException if object does not have a property with given name
+ * @throws IllegalArgumentException if value type does not fit to expected type
+ */
+ private static Object applyToProperty( Object obj, String name, Object value )
+ throws UnknownStyleException, IllegalArgumentException
+ {
+ Class> cls = obj.getClass();
+ String getterName = buildMethodName( "get", name );
+ String setterName = buildMethodName( "set", name );
+
+ try {
+ Method getter;
+ try {
+ getter = cls.getMethod( getterName );
+ } catch( NoSuchMethodException ex ) {
+ getter = cls.getMethod( buildMethodName( "is", name ) );
+ }
+ Method setter = cls.getMethod( setterName, getter.getReturnType() );
+ Object oldValue = getter.invoke( obj );
+ setter.invoke( obj, value );
+ return oldValue;
+ } catch( NoSuchMethodException ex ) {
+ throw new UnknownStyleException( name );
+ } catch( Exception ex ) {
+ throw new IllegalArgumentException( "failed to invoke property methods '" + cls.getName() + "."
+ + getterName + "()' or '" + setterName + "(...)'", ex );
+ }
+ }
+
+ private static String buildMethodName( String prefix, String name ) {
+ int prefixLength = prefix.length();
+ int nameLength = name.length();
+ char[] chars = new char[prefixLength + nameLength];
+ prefix.getChars( 0, prefixLength, chars, 0 );
+ name.getChars( 0, nameLength, chars, prefixLength );
+ chars[prefixLength] = Character.toUpperCase( chars[prefixLength] );
+ return new String( chars );
+ }
+
+ /**
+ * Applies the given value to an annotated field of the given object
+ * or to a property of the given component.
+ * The field must be annotated with {@link Styleable}.
+ * The component property must have public getter and setter methods.
+ *
+ * @param obj the object
+ * @param comp the component, or {@code null}
+ * @param key the name of the field
+ * @param value the new value
+ * @return the old value of the field
+ * @throws UnknownStyleException if object does not have a annotated field with given name
+ * @throws IllegalArgumentException if value type does not fit to expected type
+ */
+ public static Object applyToAnnotatedObjectOrComponent( Object obj, Object comp, String key, Object value ) {
+ try {
+ return applyToAnnotatedObject( obj, key, value );
+ } catch( UnknownStyleException ex ) {
+ try {
+ if( comp != null )
+ return applyToProperty( comp, key, value );
+ } catch( UnknownStyleException ex2 ) {
+ // ignore
+ }
+ throw ex;
+ }
+ }
+
+ static Object applyToAnnotatedObjectOrBorder( Object obj, String key, Object value,
+ JComponent c, AtomicBoolean borderShared )
+ {
+ try {
+ return applyToAnnotatedObject( obj, key, value );
+ } catch( UnknownStyleException ex ) {
+ // apply to border
+ Border border = c.getBorder();
+ if( border instanceof StyleableBorder ) {
+ if( borderShared.get() ) {
+ border = cloneBorder( border );
+ c.setBorder( border );
+ borderShared.set( false );
+ }
+
+ try {
+ return ((StyleableBorder)border).applyStyleProperty( key, value );
+ } catch( UnknownStyleException ex2 ) {
+ // ignore
+ }
+ }
+
+ // apply to component property
+ try {
+ return applyToProperty( c, key, value );
+ } catch( UnknownStyleException ex2 ) {
+ // ignore
+ }
+ throw ex;
+ }
+ }
+
+ public static Object getStyle( JComponent c ) {
+ return c.getClientProperty( FlatClientProperties.STYLE );
+ }
+
+ static PropertyChangeListener createPropertyChangeListener( JComponent c,
+ Consumer applyStyle, PropertyChangeListener superListener )
+ {
+ return e -> {
+ if( superListener != null )
+ superListener.propertyChange( e );
+
+ if( FlatClientProperties.STYLE.equals( e.getPropertyName() ) ) {
+ applyStyle.accept( e.getNewValue() );
+ c.revalidate();
+ c.repaint();
+ }
+ };
+ }
+
+ static Border cloneBorder( Border border ) {
+ Class extends Border> borderClass = border.getClass();
+ try {
+ return borderClass.getDeclaredConstructor().newInstance();
+ } catch( Exception ex ) {
+ throw new IllegalArgumentException( "failed to clone border '" + borderClass.getName() + "'" );
+ }
+ }
+
+ static Icon cloneIcon( Icon icon ) {
+ Class extends Icon> iconClass = icon.getClass();
+ try {
+ return iconClass.getDeclaredConstructor().newInstance();
+ } catch( Exception ex ) {
+ throw new IllegalArgumentException( "failed to clone icon '" + iconClass.getName() + "'" );
+ }
+ }
+
+ /**
+ * Returns a map of all fields annotated with {@link Styleable}.
+ * The key is the name of the field and the value the type of the field.
+ */
+ public static Map> getAnnotatedStyleableInfos( Object obj ) {
+ return getAnnotatedStyleableInfos( obj, null );
+ }
+
+ public static Map> getAnnotatedStyleableInfos( Object obj, Border border ) {
+ Map> infos = new LinkedHashMap<>();
+ collectAnnotatedStyleableInfos( obj, infos );
+ collectStyleableInfos( border, infos );
+ return infos;
+ }
+
+ /**
+ * Search for all fields annotated with {@link Styleable} and add them to the given map.
+ * The key is the name of the field and the value the type of the field.
+ */
+ public static void collectAnnotatedStyleableInfos( Object obj, Map> infos ) {
+ Class> cls = obj.getClass();
+
+ for(;;) {
+ for( Field f : cls.getDeclaredFields() ) {
+ if( !isValidField( f ) )
+ continue;
+
+ Styleable styleable = f.getAnnotation( Styleable.class );
+ if( styleable == null )
+ continue;
+
+ String name = f.getName();
+ Class> type = f.getType();
+
+ // handle "dot" keys (e.g. change field name "iconArrowType" to style key "icon.arrowType")
+ if( styleable.dot() ) {
+ int len = name.length();
+ for( int i = 0; i < len; i++ ) {
+ if( Character.isUpperCase( name.charAt( i ) ) ) {
+ name = name.substring( 0, i ) + '.'
+ + Character.toLowerCase( name.charAt( i ) )
+ + name.substring( i + 1 );
+ break;
+ }
+ }
+ }
+
+ // field has a different type
+ if( styleable.type() != Void.class )
+ type = styleable.type();
+
+ infos.put( name, type );
+ }
+
+ cls = cls.getSuperclass();
+ if( cls == null )
+ return;
+
+ String superclassName = cls.getName();
+ if( superclassName.startsWith( "java." ) || superclassName.startsWith( "javax." ) )
+ return;
+ }
+ }
+
+ public static void collectStyleableInfos( Border border, Map> infos ) {
+ if( border instanceof StyleableBorder )
+ infos.putAll( ((StyleableBorder)border).getStyleableInfos() );
+ }
+
+ public static void putAllPrefixKey( Map> infos, String keyPrefix, Map> infos2 ) {
+ for( Map.Entry> e : infos2.entrySet() )
+ infos.put( keyPrefix.concat( e.getKey() ), e.getValue() );
+ }
+
+ //---- class UnknownStyleException ----------------------------------------
+
+ public static class UnknownStyleException
+ extends IllegalArgumentException
+ {
+ public UnknownStyleException( String key ) {
+ super( key );
+ }
+
+ @Override
+ public String getMessage() {
+ return "unknown style '" + super.getMessage() + "'";
+ }
+ }
+}
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTabbedPaneUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTabbedPaneUI.java
index 906eb9e5..a63bd555 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTabbedPaneUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTabbedPaneUI.java
@@ -52,7 +52,9 @@ import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collections;
+import java.util.LinkedHashMap;
import java.util.Locale;
+import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.IntConsumer;
@@ -84,6 +86,10 @@ import javax.swing.plaf.basic.BasicTabbedPaneUI;
import javax.swing.text.JTextComponent;
import javax.swing.text.View;
import com.formdev.flatlaf.FlatLaf;
+import com.formdev.flatlaf.icons.FlatTabbedPaneCloseIcon;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.Animator;
import com.formdev.flatlaf.util.CubicBezierEasing;
import com.formdev.flatlaf.util.JavaCompatibility;
@@ -101,7 +107,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TabbedPane.font Font
* @uiDefault TabbedPane.background Color
* @uiDefault TabbedPane.foreground Color
- * @uiDefault TabbedPane.shadow Color used for scroll arrows and cropped line
+ * @uiDefault TabbedPane.shadow Color used for cropped line
* @uiDefault TabbedPane.textIconGap int
* @uiDefault TabbedPane.tabInsets Insets
* @uiDefault TabbedPane.selectedTabPadInsets Insets unused
@@ -156,6 +162,7 @@ import com.formdev.flatlaf.util.UIScale;
*/
public class FlatTabbedPaneUI
extends BasicTabbedPaneUI
+ implements StyleableUI
{
// tabs popup policy / scroll arrows policy
protected static final int NEVER = 0;
@@ -177,43 +184,43 @@ public class FlatTabbedPaneUI
private static Set focusBackwardTraversalKeys;
protected Color foreground;
- protected Color disabledForeground;
- protected Color selectedBackground;
- protected Color selectedForeground;
- protected Color underlineColor;
- protected Color disabledUnderlineColor;
- protected Color hoverColor;
- protected Color focusColor;
- protected Color tabSeparatorColor;
- protected Color contentAreaColor;
+ @Styleable protected Color disabledForeground;
+ @Styleable protected Color selectedBackground;
+ @Styleable protected Color selectedForeground;
+ @Styleable protected Color underlineColor;
+ @Styleable protected Color disabledUnderlineColor;
+ @Styleable protected Color hoverColor;
+ @Styleable protected Color focusColor;
+ @Styleable protected Color tabSeparatorColor;
+ @Styleable protected Color contentAreaColor;
private int textIconGapUnscaled;
- protected int minimumTabWidth;
- protected int maximumTabWidth;
- protected int tabHeight;
- protected int tabSelectionHeight;
- protected int contentSeparatorHeight;
- protected boolean showTabSeparators;
- protected boolean tabSeparatorsFullHeight;
- protected boolean hasFullBorder;
- protected boolean tabsOpaque = true;
+ @Styleable protected int minimumTabWidth;
+ @Styleable protected int maximumTabWidth;
+ @Styleable protected int tabHeight;
+ @Styleable protected int tabSelectionHeight;
+ @Styleable protected int contentSeparatorHeight;
+ @Styleable protected boolean showTabSeparators;
+ @Styleable protected boolean tabSeparatorsFullHeight;
+ @Styleable protected boolean hasFullBorder;
+ @Styleable protected boolean tabsOpaque = true;
- private int tabsPopupPolicy;
- private int scrollButtonsPolicy;
- private int scrollButtonsPlacement;
+ @Styleable(type=String.class) private int tabsPopupPolicy;
+ @Styleable(type=String.class) private int scrollButtonsPolicy;
+ @Styleable(type=String.class) private int scrollButtonsPlacement;
- private int tabAreaAlignment;
- private int tabAlignment;
- private int tabWidthMode;
+ @Styleable(type=String.class) private int tabAreaAlignment;
+ @Styleable(type=String.class) private int tabAlignment;
+ @Styleable(type=String.class) private int tabWidthMode;
protected Icon closeIcon;
- protected String arrowType;
- protected Insets buttonInsets;
- protected int buttonArc;
- protected Color buttonHoverBackground;
- protected Color buttonPressedBackground;
+ @Styleable protected String arrowType;
+ @Styleable protected Insets buttonInsets;
+ @Styleable protected int buttonArc;
+ @Styleable protected Color buttonHoverBackground;
+ @Styleable protected Color buttonPressedBackground;
- protected String moreTabsButtonToolTipText;
+ @Styleable protected String moreTabsButtonToolTipText;
protected JViewport tabViewport;
protected FlatWheelTabScroller wheelTabScroller;
@@ -231,6 +238,8 @@ public class FlatTabbedPaneUI
private boolean pressedTabClose;
private Object[] oldRenderingHints;
+ private Map oldStyleValues;
+ private boolean closeIconShared = true;
public static ComponentUI createUI( JComponent c ) {
return new FlatTabbedPaneUI();
@@ -259,6 +268,8 @@ public class FlatTabbedPaneUI
buttonPressedBackground = UIManager.getColor( "TabbedPane.buttonPressedBackground" );
super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( c ) );
}
@Override
@@ -313,6 +324,7 @@ public class FlatTabbedPaneUI
tabAlignment = parseAlignment( UIManager.getString( "TabbedPane.tabAlignment" ), CENTER );
tabWidthMode = parseTabWidthMode( UIManager.getString( "TabbedPane.tabWidthMode" ) );
closeIcon = UIManager.getIcon( "TabbedPane.closeIcon" );
+ closeIconShared = true;
buttonInsets = UIManager.getInsets( "TabbedPane.buttonInsets" );
buttonArc = UIManager.getInt( "TabbedPane.buttonArc" );
@@ -361,6 +373,8 @@ public class FlatTabbedPaneUI
buttonHoverBackground = null;
buttonPressedBackground = null;
+ oldStyleValues = null;
+
MigLayoutVisualPadding.uninstall( tabPane );
}
@@ -558,6 +572,78 @@ public class FlatTabbedPaneUI
return new FlatScrollableTabButton( direction );
}
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+
+ // update buttons
+ for( Component c : tabPane.getComponents() ) {
+ if( c instanceof FlatTabAreaButton )
+ ((FlatTabAreaButton)c).updateStyle();
+ }
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ // close icon
+ if( key.startsWith( "close" ) ) {
+ if( !(closeIcon instanceof FlatTabbedPaneCloseIcon) )
+ return new UnknownStyleException( key );
+
+ if( closeIconShared ) {
+ closeIcon = FlatStylingSupport.cloneIcon( closeIcon );
+ closeIconShared = false;
+ }
+
+ return ((FlatTabbedPaneCloseIcon)closeIcon).applyStyleProperty( key, value );
+ }
+
+ if( value instanceof String ) {
+ switch( key ) {
+ case "tabsPopupPolicy": value = parseTabsPopupPolicy( (String) value ); break;
+ case "scrollButtonsPolicy": value = parseScrollButtonsPolicy( (String) value ); break;
+ case "scrollButtonsPlacement": value = parseScrollButtonsPlacement( (String) value ); break;
+
+ case "tabAreaAlignment": value = parseAlignment( (String) value, LEADING ); break;
+ case "tabAlignment": value = parseAlignment( (String) value, CENTER ); break;
+ case "tabWidthMode": value = parseTabWidthMode( (String) value ); break;
+ }
+ } else {
+ Object oldValue = null;
+ switch( key ) {
+ // BasicTabbedPaneUI
+ case "tabInsets": oldValue = tabInsets; tabInsets = (Insets) value; return oldValue;
+ case "tabAreaInsets": oldValue = tabAreaInsets; tabAreaInsets = (Insets) value; return oldValue;
+ case "textIconGap":
+ oldValue = textIconGapUnscaled;
+ textIconGapUnscaled = (int) value;
+ textIconGap = scale( textIconGapUnscaled );
+ return oldValue;
+ }
+ }
+
+ return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, tabPane, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ Map> infos = new LinkedHashMap<>();
+ infos.put( "tabInsets", Insets.class );
+ infos.put( "tabAreaInsets", Insets.class );
+ infos.put( "textIconGap", int.class );
+ FlatStylingSupport.collectAnnotatedStyleableInfos( this, infos );
+ if( closeIcon instanceof FlatTabbedPaneCloseIcon )
+ infos.putAll( ((FlatTabbedPaneCloseIcon)closeIcon).getStyleableInfos() );
+ return infos;
+ }
+
protected void setRolloverTab( int x, int y ) {
setRolloverTab( tabForCoordinate( tabPane, x, y ) );
}
@@ -1567,6 +1653,12 @@ public class FlatTabbedPaneUI
setArrowWidth( 10 );
}
+ protected void updateStyle() {
+ updateStyle( arrowType,
+ FlatTabbedPaneUI.this.foreground, FlatTabbedPaneUI.this.disabledForeground,
+ null, buttonHoverBackground, null, buttonPressedBackground );
+ }
+
@Override
protected Color deriveBackground( Color background ) {
return FlatUIUtils.deriveColor( background, tabPane.getBackground() );
@@ -2351,6 +2443,12 @@ public class FlatTabbedPaneUI
tabPane.repaint();
ensureSelectedTabIsVisibleLater();
break;
+
+ case STYLE:
+ applyStyle( e.getNewValue() );
+ tabPane.revalidate();
+ tabPane.repaint();
+ break;
}
}
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableCellBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableCellBorder.java
index cd25abae..76263b12 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableCellBorder.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableCellBorder.java
@@ -16,11 +16,15 @@
package com.formdev.flatlaf.ui;
+import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
+import java.awt.Insets;
+import java.util.function.Function;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
+import javax.swing.plaf.TableUI;
/**
* Cell border for {@link javax.swing.table.DefaultTableCellRenderer}
@@ -33,12 +37,54 @@ import javax.swing.UIManager;
public class FlatTableCellBorder
extends FlatLineBorder
{
- final boolean showCellFocusIndicator = UIManager.getBoolean( "Table.showCellFocusIndicator" );
+ protected boolean showCellFocusIndicator = UIManager.getBoolean( "Table.showCellFocusIndicator" );
+
+ private Component c;
protected FlatTableCellBorder() {
super( UIManager.getInsets( "Table.cellMargins" ), UIManager.getColor( "Table.cellFocusColor" ) );
}
+ @Override
+ public Insets getBorderInsets( Component c, Insets insets ) {
+ Insets m = getStyleFromTableUI( c, ui -> ui.cellMargins );
+ if( m != null )
+ return scaleInsets( c, insets, m.top, m.left, m.bottom, m.right );
+
+ return super.getBorderInsets( c, insets );
+ }
+
+ @Override
+ public Color getLineColor() {
+ if( c != null ) {
+ Color color = getStyleFromTableUI( c, ui -> ui.cellFocusColor );
+ if( color != null )
+ return color;
+ }
+ return super.getLineColor();
+ }
+
+ @Override
+ public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
+ this.c = c;
+ super.paintBorder( c, g, x, y, width, height );
+ this.c = null;
+ }
+
+ /**
+ * Because this borders are always shared for all tables,
+ * get border specific style from FlatTableUI.
+ */
+ static T getStyleFromTableUI( Component c, Function f ) {
+ JTable table = (JTable) SwingUtilities.getAncestorOfClass( JTable.class, c );
+ if( table != null ) {
+ TableUI ui = table.getUI();
+ if( ui instanceof FlatTableUI )
+ return f.apply( (FlatTableUI) ui );
+ }
+ return null;
+ }
+
//---- class Default ------------------------------------------------------
/**
@@ -74,6 +120,9 @@ public class FlatTableCellBorder
{
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
+ Boolean b = getStyleFromTableUI( c, ui -> ui.showCellFocusIndicator );
+ boolean showCellFocusIndicator = (b != null) ? b : this.showCellFocusIndicator;
+
if( !showCellFocusIndicator ) {
JTable table = (JTable) SwingUtilities.getAncestorOfClass( JTable.class, c );
if( table != null && !isSelectionEditable( table ) )
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableHeaderBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableHeaderBorder.java
index 3c992560..30f7a06b 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableHeaderBorder.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableHeaderBorder.java
@@ -21,6 +21,7 @@ import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
+import java.awt.Insets;
import java.awt.geom.Rectangle2D;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
@@ -51,12 +52,30 @@ public class FlatTableHeaderBorder
super( UIManager.getInsets( "TableHeader.cellMargins" ) );
}
+ @Override
+ public Insets getBorderInsets( Component c, Insets insets ) {
+ JTableHeader header = (JTableHeader) SwingUtilities.getAncestorOfClass( JTableHeader.class, c );
+ if( header != null ) {
+ if( header.getUI() instanceof FlatTableHeaderUI ) {
+ FlatTableHeaderUI ui = (FlatTableHeaderUI) header.getUI();
+ if( ui.cellMargins != null ) {
+ Insets m = ui.cellMargins;
+ return scaleInsets( c, insets, m.top, m.left, m.bottom, m.right );
+ }
+ }
+ }
+
+ return super.getBorderInsets( c, insets );
+ }
+
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
JTableHeader header = (JTableHeader) SwingUtilities.getAncestorOfClass( JTableHeader.class, c );
boolean leftToRight = (header != null ? header : c).getComponentOrientation().isLeftToRight();
boolean paintLeft = !leftToRight;
boolean paintRight = leftToRight;
+ Color separatorColor = this.separatorColor;
+ Color bottomSeparatorColor = this.bottomSeparatorColor;
if( header != null ) {
int hx = SwingUtilities.convertPoint( c, x, y, header ).x;
@@ -68,6 +87,16 @@ public class FlatTableHeaderBorder
if( hx + width >= header.getWidth() && leftToRight && hideTrailingVerticalLine( header ) )
paintRight = false;
}
+
+ // Because this border is always shared for all table headers,
+ // get border specific style from FlatTableHeaderUI.
+ if( header.getUI() instanceof FlatTableHeaderUI ) {
+ FlatTableHeaderUI ui = (FlatTableHeaderUI) header.getUI();
+ if( ui.separatorColor != null )
+ separatorColor = ui.separatorColor;
+ if( ui.bottomSeparatorColor != null )
+ bottomSeparatorColor = ui.bottomSeparatorColor;
+ }
}
float lineWidth = UIScale.scale( 1f );
@@ -110,6 +139,12 @@ public class FlatTableHeaderBorder
}
protected boolean hideTrailingVerticalLine( JTableHeader header ) {
+ if( header.getUI() instanceof FlatTableHeaderUI ) {
+ FlatTableHeaderUI ui = (FlatTableHeaderUI) header.getUI();
+ if( ui.showTrailingVerticalLine )
+ return false;
+ }
+
if( showTrailingVerticalLine )
return false;
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableHeaderUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableHeaderUI.java
index 5f6f6637..d0b15bcf 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableHeaderUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableHeaderUI.java
@@ -26,7 +26,8 @@ import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
-import java.util.Objects;
+import java.beans.PropertyChangeListener;
+import java.util.Map;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
@@ -40,6 +41,9 @@ import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTableHeaderUI;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
+import com.formdev.flatlaf.FlatClientProperties;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -64,32 +68,52 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TableHeader.bottomSeparatorColor Color
* @uiDefault TableHeader.showTrailingVerticalLine boolean
*
+ *
+ *
+ * @uiDefault Component.arrowType String chevron (default) or triangle
+ * @uiDefault Table.sortIconColor Color
+ *
* @author Karl Tauber
*/
public class FlatTableHeaderUI
extends BasicTableHeaderUI
+ implements StyleableUI
{
- protected Color bottomSeparatorColor;
- protected int height;
- protected int sortIconPosition;
+ @Styleable protected Color bottomSeparatorColor;
+ @Styleable protected int height;
+ @Styleable(type=String.class) protected int sortIconPosition;
+
+ // for FlatTableHeaderBorder
+ @Styleable protected Insets cellMargins;
+ @Styleable protected Color separatorColor;
+ /** @since 2 */ @Styleable protected boolean showTrailingVerticalLine;
+
+ // for FlatAscendingSortIcon and FlatDescendingSortIcon
+ // (needs to be public because icon classes are in another package)
+ @Styleable public String arrowType;
+ @Styleable public Color sortIconColor;
+
+ private PropertyChangeListener propertyChangeListener;
+ private Map oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
return new FlatTableHeaderUI();
}
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( c ) );
+ }
+
@Override
protected void installDefaults() {
super.installDefaults();
bottomSeparatorColor = UIManager.getColor( "TableHeader.bottomSeparatorColor" );
height = UIManager.getInt( "TableHeader.height" );
- switch( Objects.toString( UIManager.getString( "TableHeader.sortIconPosition" ), "right" ) ) {
- default:
- case "right": sortIconPosition = SwingConstants.RIGHT; break;
- case "left": sortIconPosition = SwingConstants.LEFT; break;
- case "top": sortIconPosition = SwingConstants.TOP; break;
- case "bottom": sortIconPosition = SwingConstants.BOTTOM; break;
- }
+ sortIconPosition = parseSortIconPosition( UIManager.getString( "TableHeader.sortIconPosition" ) );
}
@Override
@@ -97,6 +121,62 @@ public class FlatTableHeaderUI
super.uninstallDefaults();
bottomSeparatorColor = null;
+
+ oldStyleValues = null;
+ }
+
+ @Override
+ protected void installListeners() {
+ super.installListeners();
+
+ propertyChangeListener = FlatStylingSupport.createPropertyChangeListener( header, this::applyStyle, null );
+ header.addPropertyChangeListener( FlatClientProperties.STYLE, propertyChangeListener );
+ }
+
+ @Override
+ protected void uninstallListeners() {
+ super.uninstallListeners();
+
+ header.removePropertyChangeListener( FlatClientProperties.STYLE, propertyChangeListener );
+ propertyChangeListener = null;
+ }
+
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ if( key.equals( "sortIconPosition" ) && value instanceof String )
+ value = parseSortIconPosition( (String) value );
+
+ return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, header, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
+
+ private static int parseSortIconPosition( String str ) {
+ if( str == null )
+ str = "";
+
+ switch( str ) {
+ default:
+ case "right": return SwingConstants.RIGHT;
+ case "left": return SwingConstants.LEFT;
+ case "top": return SwingConstants.TOP;
+ case "bottom": return SwingConstants.BOTTOM;
+ }
}
@Override
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableUI.java
index 8a3b7424..c45507d0 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTableUI.java
@@ -22,10 +22,12 @@ import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
+import java.awt.Insets;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeListener;
+import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
@@ -36,6 +38,8 @@ import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicTableUI;
import javax.swing.table.JTableHeader;
import com.formdev.flatlaf.FlatClientProperties;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -90,27 +94,41 @@ import com.formdev.flatlaf.util.UIScale;
*/
public class FlatTableUI
extends BasicTableUI
+ implements StyleableUI
{
protected boolean showHorizontalLines;
protected boolean showVerticalLines;
- /** @since 1.6 */ protected boolean showTrailingVerticalLine;
+ /** @since 1.6 */ @Styleable protected boolean showTrailingVerticalLine;
protected Dimension intercellSpacing;
- protected Color selectionBackground;
- protected Color selectionForeground;
- protected Color selectionInactiveBackground;
- protected Color selectionInactiveForeground;
+ @Styleable protected Color selectionBackground;
+ @Styleable protected Color selectionForeground;
+ @Styleable protected Color selectionInactiveBackground;
+ @Styleable protected Color selectionInactiveForeground;
+
+ // for FlatTableCellBorder
+ @Styleable protected Insets cellMargins;
+ @Styleable protected Color cellFocusColor;
+ @Styleable protected boolean showCellFocusIndicator;
private boolean oldShowHorizontalLines;
private boolean oldShowVerticalLines;
private Dimension oldIntercellSpacing;
private PropertyChangeListener propertyChangeListener;
+ private Map oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
return new FlatTableUI();
}
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( c ) );
+ }
+
@Override
protected void installDefaults() {
super.installDefaults();
@@ -155,6 +173,8 @@ public class FlatTableUI
selectionInactiveBackground = null;
selectionInactiveForeground = null;
+ oldStyleValues = null;
+
// restore old show horizontal/vertical lines (if not modified)
if( !showHorizontalLines && oldShowHorizontalLines && !table.getShowHorizontalLines() )
table.setShowHorizontalLines( true );
@@ -171,8 +191,17 @@ public class FlatTableUI
super.installListeners();
propertyChangeListener = e -> {
- if( FlatClientProperties.COMPONENT_FOCUS_OWNER.equals( e.getPropertyName() ) )
- toggleSelectionColors();
+ switch( e.getPropertyName() ) {
+ case FlatClientProperties.COMPONENT_FOCUS_OWNER:
+ toggleSelectionColors();
+ break;
+
+ case FlatClientProperties.STYLE:
+ applyStyle( e.getNewValue() );
+ table.revalidate();
+ table.repaint();
+ break;
+ }
};
table.addPropertyChangeListener( propertyChangeListener );
}
@@ -206,6 +235,51 @@ public class FlatTableUI
};
}
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ Color oldSelectionBackground = selectionBackground;
+ Color oldSelectionForeground = selectionForeground;
+ Color oldSelectionInactiveBackground = selectionInactiveBackground;
+ Color oldSelectionInactiveForeground = selectionInactiveForeground;
+
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+
+ // update selection background
+ if( selectionBackground != oldSelectionBackground ) {
+ Color selBg = table.getSelectionBackground();
+ if( selBg == oldSelectionBackground )
+ table.setSelectionBackground( selectionBackground );
+ else if( selBg == oldSelectionInactiveBackground )
+ table.setSelectionBackground( selectionInactiveBackground );
+ }
+
+ // update selection foreground
+ if( selectionForeground != oldSelectionForeground ) {
+ Color selFg = table.getSelectionForeground();
+ if( selFg == oldSelectionForeground )
+ table.setSelectionForeground( selectionForeground );
+ else if( selFg == oldSelectionInactiveForeground )
+ table.setSelectionForeground( selectionInactiveForeground );
+ }
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, table, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
+
/**
* Toggle selection colors from focused to inactive and vice versa.
*
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextAreaUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextAreaUI.java
index f188353f..14b3f04b 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextAreaUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextAreaUI.java
@@ -23,13 +23,14 @@ import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.event.FocusListener;
import java.beans.PropertyChangeEvent;
+import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
-import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTextAreaUI;
-import javax.swing.text.JTextComponent;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
/**
@@ -60,17 +61,22 @@ import com.formdev.flatlaf.util.HiDPIUtils;
*/
public class FlatTextAreaUI
extends BasicTextAreaUI
+ implements StyleableUI
{
- protected int minimumWidth;
+ @Styleable protected int minimumWidth;
protected boolean isIntelliJTheme;
- protected Color background;
- protected Color disabledBackground;
- protected Color inactiveBackground;
- protected Color focusedBackground;
+ private Color background;
+ @Styleable protected Color disabledBackground;
+ @Styleable protected Color inactiveBackground;
+ @Styleable protected Color focusedBackground;
+
+ private Color oldDisabledBackground;
+ private Color oldInactiveBackground;
private Insets defaultMargin;
private FocusListener focusListener;
+ private Map oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
return new FlatTextAreaUI();
@@ -80,7 +86,7 @@ public class FlatTextAreaUI
public void installUI( JComponent c ) {
super.installUI( c );
- updateBackground();
+ applyStyle( FlatStylingSupport.getStyle( c ) );
}
@Override
@@ -105,6 +111,11 @@ public class FlatTextAreaUI
disabledBackground = null;
inactiveBackground = null;
focusedBackground = null;
+
+ oldDisabledBackground = null;
+ oldInactiveBackground = null;
+
+ oldStyleValues = null;
}
@Override
@@ -126,38 +137,46 @@ public class FlatTextAreaUI
@Override
protected void propertyChange( PropertyChangeEvent e ) {
- super.propertyChange( e );
- FlatEditorPaneUI.propertyChange( getComponent(), e );
+ // invoke updateBackground() before super.propertyChange()
+ String propertyName = e.getPropertyName();
+ if( "editable".equals( propertyName ) || "enabled".equals( propertyName ) )
+ updateBackground();
- switch( e.getPropertyName() ) {
- case "editable":
- case "enabled":
- updateBackground();
- break;
- }
+ super.propertyChange( e );
+ FlatEditorPaneUI.propertyChange( getComponent(), e, this::applyStyle );
+ }
+
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldDisabledBackground = disabledBackground;
+ oldInactiveBackground = inactiveBackground;
+
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+
+ updateBackground();
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, getComponent(), key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
}
private void updateBackground() {
- JTextComponent c = getComponent();
-
- Color background = c.getBackground();
- if( !(background instanceof UIResource) )
- return;
-
- // do not update background if it currently has a unknown color (assigned from outside)
- if( background != this.background &&
- background != disabledBackground &&
- background != inactiveBackground )
- return;
-
- Color newBackground = !c.isEnabled()
- ? disabledBackground
- : (!c.isEditable()
- ? inactiveBackground
- : this.background);
-
- if( newBackground != background )
- c.setBackground( newBackground );
+ FlatTextFieldUI.updateBackground( getComponent(), background,
+ disabledBackground, inactiveBackground,
+ oldDisabledBackground, oldInactiveBackground );
}
@Override
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextBorder.java
index b57a1088..e446e670 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextBorder.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextBorder.java
@@ -18,6 +18,7 @@ package com.formdev.flatlaf.ui;
import java.awt.Component;
import javax.swing.UIManager;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
/**
* Border for various text components (e.g. {@link javax.swing.JTextField}).
@@ -29,7 +30,7 @@ import javax.swing.UIManager;
public class FlatTextBorder
extends FlatBorder
{
- protected final int arc = UIManager.getInt( "TextComponent.arc" );
+ @Styleable protected int arc = UIManager.getInt( "TextComponent.arc" );
@Override
protected int getArc( Component c ) {
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextFieldUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextFieldUI.java
index 5a02d059..f3fc6a58 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextFieldUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextFieldUI.java
@@ -27,7 +27,10 @@ import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.FocusListener;
import java.beans.PropertyChangeEvent;
+import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JSpinner;
@@ -40,6 +43,8 @@ import javax.swing.plaf.basic.BasicTextFieldUI;
import javax.swing.text.Caret;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.FlatClientProperties;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.JavaCompatibility;
import com.formdev.flatlaf.util.UIScale;
@@ -75,20 +80,36 @@ import com.formdev.flatlaf.util.UIScale;
*/
public class FlatTextFieldUI
extends BasicTextFieldUI
+ implements StyleableUI
{
- protected int minimumWidth;
+ @Styleable protected int minimumWidth;
protected boolean isIntelliJTheme;
- protected Color placeholderForeground;
- protected Color focusedBackground;
+ private Color background;
+ @Styleable protected Color disabledBackground;
+ @Styleable protected Color inactiveBackground;
+ @Styleable protected Color placeholderForeground;
+ @Styleable protected Color focusedBackground;
+
+ private Color oldDisabledBackground;
+ private Color oldInactiveBackground;
private Insets defaultMargin;
private FocusListener focusListener;
+ private Map oldStyleValues;
+ private AtomicBoolean borderShared;
public static ComponentUI createUI( JComponent c ) {
return new FlatTextFieldUI();
}
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( c ) );
+ }
+
@Override
protected void installDefaults() {
super.installDefaults();
@@ -96,6 +117,9 @@ public class FlatTextFieldUI
String prefix = getPropertyPrefix();
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
+ background = UIManager.getColor( prefix + ".background" );
+ disabledBackground = UIManager.getColor( prefix + ".disabledBackground" );
+ inactiveBackground = UIManager.getColor( prefix + ".inactiveBackground" );
placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" );
focusedBackground = UIManager.getColor( prefix + ".focusedBackground" );
@@ -110,9 +134,18 @@ public class FlatTextFieldUI
protected void uninstallDefaults() {
super.uninstallDefaults();
+ background = null;
+ disabledBackground = null;
+ inactiveBackground = null;
placeholderForeground = null;
focusedBackground = null;
+ oldDisabledBackground = null;
+ oldInactiveBackground = null;
+
+ oldStyleValues = null;
+ borderShared = null;
+
MigLayoutVisualPadding.uninstall( getComponent() );
}
@@ -141,11 +174,15 @@ public class FlatTextFieldUI
@Override
protected void propertyChange( PropertyChangeEvent e ) {
- super.propertyChange( e );
- propertyChange( getComponent(), e );
+ String propertyName = e.getPropertyName();
+ if( "editable".equals( propertyName ) || "enabled".equals( propertyName ) )
+ updateBackground();
+ else
+ super.propertyChange( e );
+ propertyChange( getComponent(), e, this::applyStyle );
}
- static void propertyChange( JTextComponent c, PropertyChangeEvent e ) {
+ static void propertyChange( JTextComponent c, PropertyChangeEvent e, Consumer applyStyle ) {
switch( e.getPropertyName() ) {
case FlatClientProperties.PLACEHOLDER_TEXT:
case FlatClientProperties.COMPONENT_ROUND_RECT:
@@ -156,9 +193,77 @@ public class FlatTextFieldUI
case FlatClientProperties.MINIMUM_WIDTH:
c.revalidate();
break;
+
+ case FlatClientProperties.STYLE:
+ applyStyle.accept( e.getNewValue() );
+ c.revalidate();
+ c.repaint();
+ break;
}
}
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldDisabledBackground = disabledBackground;
+ oldInactiveBackground = inactiveBackground;
+
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+
+ updateBackground();
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ if( borderShared == null )
+ borderShared = new AtomicBoolean( true );
+ return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, getComponent(), borderShared );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this, getComponent().getBorder() );
+ }
+
+ private void updateBackground() {
+ updateBackground( getComponent(), background,
+ disabledBackground, inactiveBackground,
+ oldDisabledBackground, oldInactiveBackground );
+ }
+
+ // same functionality as BasicTextUI.updateBackground()
+ static void updateBackground( JTextComponent c, Color background,
+ Color disabledBackground, Color inactiveBackground,
+ Color oldDisabledBackground, Color oldInactiveBackground )
+ {
+ Color oldBackground = c.getBackground();
+ if( !(oldBackground instanceof UIResource) )
+ return;
+
+ // do not update background if it currently has a unknown color (assigned from outside)
+ if( oldBackground != background &&
+ oldBackground != disabledBackground &&
+ oldBackground != inactiveBackground &&
+ oldBackground != oldDisabledBackground &&
+ oldBackground != oldInactiveBackground )
+ return;
+
+ Color newBackground = !c.isEnabled()
+ ? disabledBackground
+ : (!c.isEditable()
+ ? inactiveBackground
+ : background);
+
+ if( newBackground != oldBackground )
+ c.setBackground( newBackground );
+ }
+
@Override
protected void paintSafely( Graphics g ) {
paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground );
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextPaneUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextPaneUI.java
index b2962876..eadc6eda 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextPaneUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextPaneUI.java
@@ -23,11 +23,14 @@ import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.event.FocusListener;
import java.beans.PropertyChangeEvent;
+import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicTextPaneUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
/**
@@ -58,20 +61,35 @@ import com.formdev.flatlaf.util.HiDPIUtils;
*/
public class FlatTextPaneUI
extends BasicTextPaneUI
+ implements StyleableUI
{
- protected int minimumWidth;
+ @Styleable protected int minimumWidth;
protected boolean isIntelliJTheme;
- protected Color focusedBackground;
+ private Color background;
+ @Styleable protected Color disabledBackground;
+ @Styleable protected Color inactiveBackground;
+ @Styleable protected Color focusedBackground;
+
+ private Color oldDisabledBackground;
+ private Color oldInactiveBackground;
private Insets defaultMargin;
private Object oldHonorDisplayProperties;
private FocusListener focusListener;
+ private Map oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
return new FlatTextPaneUI();
}
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( c ) );
+ }
+
@Override
protected void installDefaults() {
super.installDefaults();
@@ -79,6 +97,9 @@ public class FlatTextPaneUI
String prefix = getPropertyPrefix();
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
+ background = UIManager.getColor( prefix + ".background" );
+ disabledBackground = UIManager.getColor( prefix + ".disabledBackground" );
+ inactiveBackground = UIManager.getColor( prefix + ".inactiveBackground" );
focusedBackground = UIManager.getColor( prefix + ".focusedBackground" );
defaultMargin = UIManager.getInsets( prefix + ".margin" );
@@ -92,8 +113,16 @@ public class FlatTextPaneUI
protected void uninstallDefaults() {
super.uninstallDefaults();
+ background = null;
+ disabledBackground = null;
+ inactiveBackground = null;
focusedBackground = null;
+ oldDisabledBackground = null;
+ oldInactiveBackground = null;
+
+ oldStyleValues = null;
+
getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, oldHonorDisplayProperties );
}
@@ -116,8 +145,46 @@ public class FlatTextPaneUI
@Override
protected void propertyChange( PropertyChangeEvent e ) {
+ // invoke updateBackground() before super.propertyChange()
+ String propertyName = e.getPropertyName();
+ if( "editable".equals( propertyName ) || "enabled".equals( propertyName ) )
+ updateBackground();
+
super.propertyChange( e );
- FlatEditorPaneUI.propertyChange( getComponent(), e );
+ FlatEditorPaneUI.propertyChange( getComponent(), e, this::applyStyle );
+ }
+
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldDisabledBackground = disabledBackground;
+ oldInactiveBackground = inactiveBackground;
+
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+
+ updateBackground();
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, getComponent(), key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
+
+ private void updateBackground() {
+ FlatTextFieldUI.updateBackground( getComponent(), background,
+ disabledBackground, inactiveBackground,
+ oldDisabledBackground, oldInactiveBackground );
}
@Override
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java
index 207b05b2..b7ca5baf 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java
@@ -877,6 +877,10 @@ debug*/
protected class FlatTitleLabelUI
extends FlatLabelUI
{
+ protected FlatTitleLabelUI() {
+ super( false );
+ }
+
@Override
protected void paintEnabledText( JLabel l, Graphics g, String s, int textX, int textY ) {
boolean hasEmbeddedMenuBar = hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() );
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatToggleButtonUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatToggleButtonUI.java
index 2d1a5f24..ef87d6bd 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatToggleButtonUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatToggleButtonUI.java
@@ -21,11 +21,15 @@ import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.beans.PropertyChangeEvent;
+import java.util.Iterator;
+import java.util.Map;
import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.JToggleButton;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -73,17 +77,23 @@ import com.formdev.flatlaf.util.UIScale;
public class FlatToggleButtonUI
extends FlatButtonUI
{
- protected int tabUnderlineHeight;
- protected Color tabUnderlineColor;
- protected Color tabDisabledUnderlineColor;
- protected Color tabSelectedBackground;
- protected Color tabHoverBackground;
- protected Color tabFocusBackground;
+ @Styleable(dot=true) protected int tabUnderlineHeight;
+ @Styleable(dot=true) protected Color tabUnderlineColor;
+ @Styleable(dot=true) protected Color tabDisabledUnderlineColor;
+ @Styleable(dot=true) protected Color tabSelectedBackground;
+ @Styleable(dot=true) protected Color tabHoverBackground;
+ @Styleable(dot=true) protected Color tabFocusBackground;
private boolean defaults_initialized = false;
public static ComponentUI createUI( JComponent c ) {
- return FlatUIUtils.createSharedUI( FlatToggleButtonUI.class, FlatToggleButtonUI::new );
+ return FlatUIUtils.canUseSharedUI( c )
+ ? FlatUIUtils.createSharedUI( FlatToggleButtonUI.class, () -> new FlatToggleButtonUI( true ) )
+ : new FlatToggleButtonUI( false );
+ }
+
+ protected FlatToggleButtonUI( boolean shared ) {
+ super( shared );
}
@Override
@@ -136,6 +146,31 @@ public class FlatToggleButtonUI
}
}
+ /**
+ * @since 2
+ */
+ @Override
+ protected Object applyStyleProperty( AbstractButton b, String key, Object value ) {
+ if( key.startsWith( "help." ) )
+ throw new UnknownStyleException( key );
+
+ return super.applyStyleProperty( b, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ Map> infos = super.getStyleableInfos( c );
+ Iterator it = infos.keySet().iterator();
+ while( it.hasNext() ) {
+ if( it.next().startsWith( "help." ) )
+ it.remove();
+ }
+ return infos;
+ }
+
static boolean isTabButton( Component c ) {
return c instanceof JToggleButton && clientPropertyEquals( (JToggleButton) c, BUTTON_TYPE, BUTTON_TYPE_TAB );
}
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatToolBarBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatToolBarBorder.java
index 5f7ffa57..68b9325d 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatToolBarBorder.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatToolBarBorder.java
@@ -22,9 +22,11 @@ import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
+import java.util.function.Function;
import javax.swing.JToolBar;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
+import javax.swing.plaf.ToolBarUI;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -42,7 +44,7 @@ public class FlatToolBarBorder
private static final int DOT_SIZE = 2;
private static final int GRIP_SIZE = DOT_SIZE * 3;
- protected final Color gripColor = UIManager.getColor( "ToolBar.gripColor" );
+ protected Color gripColor = UIManager.getColor( "ToolBar.gripColor" );
public FlatToolBarBorder() {
super( UIManager.getInsets( "ToolBar.borderMargins" ) );
@@ -56,7 +58,8 @@ public class FlatToolBarBorder
try {
FlatUIUtils.setRenderingHints( g2 );
- g2.setColor( gripColor );
+ Color color = getStyleFromToolBarUI( c, ui -> ui.gripColor );
+ g2.setColor( (color != null) ? color : gripColor );
paintGrip( c, g2, x, y, width, height );
} finally {
g2.dispose();
@@ -90,7 +93,14 @@ public class FlatToolBarBorder
@Override
public Insets getBorderInsets( Component c, Insets insets ) {
- insets = super.getBorderInsets( c, insets );
+ Insets m = getStyleFromToolBarUI( c, ui -> ui.borderMargins );
+ if( m != null ) {
+ int t = top, l = left, b = bottom, r = right;
+ top = m.top; left = m.left; bottom = m.bottom; right = m.right;
+ insets = super.getBorderInsets( c, insets );
+ top = t; left = l; bottom = b; right = r;
+ } else
+ insets = super.getBorderInsets( c, insets );
// add grip inset if floatable
if( c instanceof JToolBar && ((JToolBar)c).isFloatable() ) {
@@ -106,4 +116,17 @@ public class FlatToolBarBorder
return insets;
}
+
+ /**
+ * Because this border is shared for all toolbars,
+ * get border specific style from FlatToolBarUI.
+ */
+ static T getStyleFromToolBarUI( Component c, Function f ) {
+ if( c instanceof JToolBar ) {
+ ToolBarUI ui = ((JToolBar)c).getUI();
+ if( ui instanceof FlatToolBarUI )
+ return f.apply( (FlatToolBarUI) ui );
+ }
+ return null;
+ }
}
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatToolBarSeparatorUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatToolBarSeparatorUI.java
index d30eb30c..31881e2b 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatToolBarSeparatorUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatToolBarSeparatorUI.java
@@ -22,6 +22,8 @@ import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
+import java.beans.PropertyChangeListener;
+import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JSeparator;
import javax.swing.JToolBar;
@@ -29,6 +31,9 @@ import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicToolBarSeparatorUI;
+import com.formdev.flatlaf.FlatClientProperties;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JToolBar.Separator}.
@@ -42,16 +47,36 @@ import javax.swing.plaf.basic.BasicToolBarSeparatorUI;
*/
public class FlatToolBarSeparatorUI
extends BasicToolBarSeparatorUI
+ implements StyleableUI
{
private static final int LINE_WIDTH = 1;
- protected int separatorWidth;
- protected Color separatorColor;
+ @Styleable protected int separatorWidth;
+ @Styleable protected Color separatorColor;
+ private final boolean shared;
private boolean defaults_initialized = false;
+ private PropertyChangeListener propertyChangeListener;
+ private Map oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
- return FlatUIUtils.createSharedUI( FlatToolBarSeparatorUI.class, FlatToolBarSeparatorUI::new );
+ return FlatUIUtils.canUseSharedUI( c )
+ ? FlatUIUtils.createSharedUI( FlatToolBarSeparatorUI.class, () -> new FlatToolBarSeparatorUI( true ) )
+ : new FlatToolBarSeparatorUI( false );
+ }
+
+ /**
+ * @since 2
+ */
+ protected FlatToolBarSeparatorUI( boolean shared ) {
+ this.shared = shared;
+ }
+
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( c ) );
}
@Override
@@ -73,7 +98,59 @@ public class FlatToolBarSeparatorUI
@Override
protected void uninstallDefaults( JSeparator s ) {
super.uninstallDefaults( s );
+
defaults_initialized = false;
+ oldStyleValues = null;
+ }
+
+ @Override
+ protected void installListeners( JSeparator s ) {
+ super.installListeners( s );
+
+ propertyChangeListener = FlatStylingSupport.createPropertyChangeListener(
+ s, style -> applyStyle( s, this, style ), null );
+ s.addPropertyChangeListener( FlatClientProperties.STYLE, propertyChangeListener );
+ }
+
+ @Override
+ protected void uninstallListeners( JSeparator s ) {
+ super.uninstallListeners( s );
+
+ s.removePropertyChangeListener( FlatClientProperties.STYLE, propertyChangeListener );
+ propertyChangeListener = null;
+ }
+
+ private static void applyStyle( JSeparator s, FlatToolBarSeparatorUI ui, Object style ) {
+ if( style != null && ui.shared ) {
+ // unshare component UI if necessary
+ // updateUI() invokes applyStyle() from installUI()
+ s.updateUI();
+ } else
+ ui.applyStyle( style );
+ s.revalidate();
+ s.repaint();
+ }
+
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
}
@Override
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatToolBarUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatToolBarUI.java
index 86af0906..da64ed28 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatToolBarUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatToolBarUI.java
@@ -16,16 +16,21 @@
package com.formdev.flatlaf.ui;
+import java.awt.Color;
import java.awt.Component;
import java.awt.Insets;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
+import java.beans.PropertyChangeListener;
+import java.util.Map;
import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicToolBarUI;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JToolBar}.
@@ -46,13 +51,25 @@ import javax.swing.plaf.basic.BasicToolBarUI;
*
* @uiDefault ToolBar.focusableButtons boolean
*
+ *
+ *
+ * @uiDefault ToolBar.borderMargins Insets
+ * @uiDefault ToolBar.gripColor Color
+ *
* @author Karl Tauber
*/
public class FlatToolBarUI
extends BasicToolBarUI
+ implements StyleableUI
{
/** @since 1.4 */
- protected boolean focusableButtons;
+ @Styleable protected boolean focusableButtons;
+
+ // for FlatToolBarBorder
+ @Styleable protected Insets borderMargins;
+ @Styleable protected Color gripColor;
+
+ private Map oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
return new FlatToolBarUI();
@@ -65,6 +82,8 @@ public class FlatToolBarUI
// disable focusable state of buttons (when switching from another Laf)
if( !focusableButtons )
setButtonsFocusable( false );
+
+ applyStyle( FlatStylingSupport.getStyle( c ) );
}
@Override
@@ -74,6 +93,8 @@ public class FlatToolBarUI
// re-enable focusable state of buttons (when switching to another Laf)
if( !focusableButtons )
setButtonsFocusable( true );
+
+ oldStyleValues = null;
}
@Override
@@ -110,6 +131,38 @@ public class FlatToolBarUI
};
}
+ @Override
+ protected PropertyChangeListener createPropertyListener() {
+ return FlatStylingSupport.createPropertyChangeListener( toolBar, this::applyStyle, super.createPropertyListener() );
+ }
+
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ boolean oldFocusableButtons = focusableButtons;
+
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+
+ if( focusableButtons != oldFocusableButtons )
+ setButtonsFocusable( focusableButtons );
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, toolBar, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
+
/**
* @since 1.4
*/
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTreeUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTreeUI.java
index ebdb2b40..0bc07516 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTreeUI.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTreeUI.java
@@ -26,6 +26,7 @@ import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.beans.PropertyChangeListener;
+import java.util.Map;
import javax.swing.CellRendererPane;
import javax.swing.Icon;
import javax.swing.JComponent;
@@ -39,6 +40,8 @@ import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicTreeUI;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreePath;
+import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
+import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -94,25 +97,68 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Tree.wideSelection boolean
* @uiDefault Tree.showCellFocusIndicator boolean
*
+ *
+ *
+ * @uiDefault Component.arrowType String chevron (default) or triangle
+ * @uiDefault Tree.icon.expandedColor Color
+ *
+ *
+ *
+ * @uiDefault Component.arrowType String chevron (default) or triangle
+ * @uiDefault Tree.icon.collapsedColor Color
+ *
+ *
+ *
+ * @uiDefault Tree.icon.leafColor Color
+ *
+ *
+ *
+ * @uiDefault Tree.icon.closedColor Color
+ *
+ *
+ *
+ * @uiDefault Tree.icon.openColor Color
+ *
* @author Karl Tauber
*/
public class FlatTreeUI
extends BasicTreeUI
+ implements StyleableUI
{
- protected Color selectionBackground;
- protected Color selectionForeground;
- protected Color selectionInactiveBackground;
- protected Color selectionInactiveForeground;
- protected Color selectionBorderColor;
- protected boolean wideSelection;
- protected boolean showCellFocusIndicator;
+ @Styleable protected Color selectionBackground;
+ @Styleable protected Color selectionForeground;
+ @Styleable protected Color selectionInactiveBackground;
+ @Styleable protected Color selectionInactiveForeground;
+ @Styleable protected Color selectionBorderColor;
+ @Styleable protected boolean wideSelection;
+ @Styleable protected boolean showCellFocusIndicator;
+
+ // for icons
+ // (needs to be public because icon classes are in another package)
+ @Styleable(dot=true) public String iconArrowType;
+ @Styleable(dot=true) public Color iconExpandedColor;
+ @Styleable(dot=true) public Color iconCollapsedColor;
+ @Styleable(dot=true) public Color iconLeafColor;
+ @Styleable(dot=true) public Color iconClosedColor;
+ @Styleable(dot=true) public Color iconOpenColor;
private Color defaultCellNonSelectionBackground;
+ private Color defaultSelectionBackground;
+ private Color defaultSelectionForeground;
+ private Color defaultSelectionBorderColor;
+ private Map oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
return new FlatTreeUI();
}
+ @Override
+ public void installUI( JComponent c ) {
+ super.installUI( c );
+
+ applyStyle( FlatStylingSupport.getStyle( c ) );
+ }
+
@Override
protected void installDefaults() {
super.installDefaults();
@@ -128,6 +174,9 @@ public class FlatTreeUI
showCellFocusIndicator = UIManager.getBoolean( "Tree.showCellFocusIndicator" );
defaultCellNonSelectionBackground = UIManager.getColor( "Tree.textBackground" );
+ defaultSelectionBackground = selectionBackground;
+ defaultSelectionForeground = selectionForeground;
+ defaultSelectionBorderColor = selectionBorderColor;
// scale
int rowHeight = FlatUIUtils.getUIInt( "Tree.rowHeight", 16 );
@@ -150,6 +199,10 @@ public class FlatTreeUI
selectionBorderColor = null;
defaultCellNonSelectionBackground = null;
+ defaultSelectionBackground = null;
+ defaultSelectionForeground = null;
+ defaultSelectionBorderColor = null;
+ oldStyleValues = null;
}
@Override
@@ -216,6 +269,12 @@ public class FlatTreeUI
repaintWideDropLocation( tree.getDropLocation() );
}
break;
+
+ case STYLE:
+ applyStyle( e.getNewValue() );
+ tree.revalidate();
+ tree.repaint();
+ break;
}
}
};
@@ -250,6 +309,28 @@ public class FlatTreeUI
return bounds;
}
+ /**
+ * @since 2
+ */
+ protected void applyStyle( Object style ) {
+ oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
+ }
+
+ /**
+ * @since 2
+ */
+ protected Object applyStyleProperty( String key, Object value ) {
+ return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, tree, key, value );
+ }
+
+ /**
+ * @since 2
+ */
+ @Override
+ public Map> getStyleableInfos( JComponent c ) {
+ return FlatStylingSupport.getAnnotatedStyleableInfos( this );
+ }
+
/**
* Same as super.paintRow(), but supports wide selection and uses
* inactive selection background/foreground if tree is not focused.
@@ -289,35 +370,32 @@ public class FlatTreeUI
Component rendererComponent = currentCellRenderer.getTreeCellRendererComponent( tree,
path.getLastPathComponent(), isSelected, isExpanded, isLeaf, row, cellHasFocus );
- // apply inactive selection background/foreground if tree is not focused
+ // renderer background/foreground
Color oldBackgroundSelectionColor = null;
if( isSelected && !hasFocus && !isDropRow ) {
- if( rendererComponent instanceof DefaultTreeCellRenderer ) {
- DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) rendererComponent;
- if( renderer.getBackgroundSelectionColor() == selectionBackground ) {
- oldBackgroundSelectionColor = renderer.getBackgroundSelectionColor();
- renderer.setBackgroundSelectionColor( selectionInactiveBackground );
- }
- } else {
- if( rendererComponent.getBackground() == selectionBackground )
- rendererComponent.setBackground( selectionInactiveBackground );
- }
+ // apply inactive selection background/foreground if tree is not focused
+ oldBackgroundSelectionColor = setRendererBackgroundSelectionColor( rendererComponent, selectionInactiveBackground );
+ setRendererForeground( rendererComponent, selectionInactiveForeground );
- if( rendererComponent.getForeground() == selectionForeground )
- rendererComponent.setForeground( selectionInactiveForeground );
+ } else if( isSelected ) {
+ // update background/foreground if set via style
+ if( selectionBackground != defaultSelectionBackground )
+ oldBackgroundSelectionColor = setRendererBackgroundSelectionColor( rendererComponent, selectionBackground );
+ if( selectionForeground != defaultSelectionForeground )
+ setRendererForeground( rendererComponent, selectionForeground );
}
- // remove focus selection border if exactly one item is selected
+ // update focus selection border
Color oldBorderSelectionColor = null;
if( isSelected && hasFocus &&
- (!showCellFocusIndicator || tree.getMinSelectionRow() == tree.getMaxSelectionRow()) &&
- rendererComponent instanceof DefaultTreeCellRenderer )
+ (!showCellFocusIndicator || tree.getMinSelectionRow() == tree.getMaxSelectionRow()) )
{
- DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) rendererComponent;
- if( renderer.getBorderSelectionColor() == selectionBorderColor ) {
- oldBorderSelectionColor = renderer.getBorderSelectionColor();
- renderer.setBorderSelectionColor( null );
- }
+ // remove focus selection border if exactly one item is selected or if showCellFocusIndicator is false
+ oldBorderSelectionColor = setRendererBorderSelectionColor( rendererComponent, null );
+
+ } else if( hasFocus && selectionBorderColor != defaultSelectionBorderColor ) {
+ // update focus selection border if set via style
+ oldBorderSelectionColor = setRendererBorderSelectionColor( rendererComponent, selectionBorderColor );
}
// paint selection background
@@ -365,6 +443,42 @@ public class FlatTreeUI
((DefaultTreeCellRenderer)rendererComponent).setBorderSelectionColor( oldBorderSelectionColor );
}
+ private Color setRendererBackgroundSelectionColor( Component rendererComponent, Color color ) {
+ Color oldColor = null;
+
+ if( rendererComponent instanceof DefaultTreeCellRenderer ) {
+ DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) rendererComponent;
+ if( renderer.getBackgroundSelectionColor() == defaultSelectionBackground ) {
+ oldColor = renderer.getBackgroundSelectionColor();
+ renderer.setBackgroundSelectionColor( color );
+ }
+ } else {
+ if( rendererComponent.getBackground() == defaultSelectionBackground )
+ rendererComponent.setBackground( color );
+ }
+
+ return oldColor;
+ }
+
+ private void setRendererForeground( Component rendererComponent, Color color ) {
+ if( rendererComponent.getForeground() == defaultSelectionForeground )
+ rendererComponent.setForeground( color );
+ }
+
+ private Color setRendererBorderSelectionColor( Component rendererComponent, Color color ) {
+ Color oldColor = null;
+
+ if( rendererComponent instanceof DefaultTreeCellRenderer ) {
+ DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) rendererComponent;
+ if( renderer.getBorderSelectionColor() == defaultSelectionBorderColor ) {
+ oldColor = renderer.getBorderSelectionColor();
+ renderer.setBorderSelectionColor( color );
+ }
+ }
+
+ return oldColor;
+ }
+
private void paintWideSelection( Graphics g, Rectangle clipBounds, Insets insets, Rectangle bounds,
TreePath path, int row, boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf )
{
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java
index b3a9407e..c0dae004 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java
@@ -863,6 +863,14 @@ debug*/
.computeIfAbsent( key, k -> newInstanceSupplier.get() );
}
+ /**
+ * Returns whether the component UI for the given component can be shared
+ * with other components. This is only possible if it does not have styles.
+ */
+ public static boolean canUseSharedUI( JComponent c ) {
+ return FlatStylingSupport.getStyle( c ) == null;
+ }
+
//---- class RepaintFocusListener -----------------------------------------
public static class RepaintFocusListener
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/MigLayoutVisualPadding.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/MigLayoutVisualPadding.java
index 6148f718..7df37989 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/MigLayoutVisualPadding.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/MigLayoutVisualPadding.java
@@ -21,6 +21,7 @@ import java.awt.Insets;
import java.beans.PropertyChangeListener;
import java.util.function.Function;
import javax.swing.JComponent;
+import com.formdev.flatlaf.FlatClientProperties;
/**
* Support for MigLayout visual paddings.
@@ -80,7 +81,7 @@ public class MigLayoutVisualPadding
return new Insets( focusWidth, focusWidth, focusWidth, focusWidth );
} else
return null;
- }, "border" );
+ }, "border", FlatClientProperties.STYLE );
}
/**
diff --git a/flatlaf-core/src/test/java/com/formdev/flatlaf/TestUIDefaultsLoader.java b/flatlaf-core/src/test/java/com/formdev/flatlaf/TestUIDefaultsLoader.java
new file mode 100644
index 00000000..b940debe
--- /dev/null
+++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/TestUIDefaultsLoader.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2021 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;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Insets;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author Karl Tauber
+ */
+public class TestUIDefaultsLoader
+{
+ @Test
+ void parseValue() {
+ assertEquals( null, UIDefaultsLoader.parseValue( "dummy", "null", null ) );
+ assertEquals( false, UIDefaultsLoader.parseValue( "dummy", "false", null ) );
+ assertEquals( true, UIDefaultsLoader.parseValue( "dummy", "true", null ) );
+
+ assertEquals( "hello", UIDefaultsLoader.parseValue( "dummy", "hello", null ) );
+ assertEquals( "hello", UIDefaultsLoader.parseValue( "dummy", "\"hello\"", null ) );
+ assertEquals( "null", UIDefaultsLoader.parseValue( "dummy", "\"null\"", null ) );
+
+ assertEquals( 'a', UIDefaultsLoader.parseValue( "dummyChar", "a", null ) );
+ assertEquals( 123, UIDefaultsLoader.parseValue( "dummy", "123", null ) );
+ assertEquals( 123, UIDefaultsLoader.parseValue( "dummyWidth", "123", null ) );
+ assertEquals( 1.23f, UIDefaultsLoader.parseValue( "dummy", "1.23", null ) );
+ assertEquals( 1.23f, UIDefaultsLoader.parseValue( "dummyWidth", "{float}1.23", null ) );
+
+ assertEquals( new Insets( 2,2,2,2 ), UIDefaultsLoader.parseValue( "dummyInsets", "2,2,2,2", null ) );
+ assertEquals( new Dimension( 2,2 ), UIDefaultsLoader.parseValue( "dummySize", "2,2", null ) );
+ assertEquals( new Color( 0xff0000 ), UIDefaultsLoader.parseValue( "dummy", "#f00", null ) );
+ assertEquals( new Color( 0xff0000 ), UIDefaultsLoader.parseValue( "dummyColor", "#f00", null ) );
+ }
+
+ @Test
+ void parseValueWithJavaType() {
+ assertEquals( null, UIDefaultsLoader.parseValue( "dummy", "null", String.class ) );
+ assertEquals( false, UIDefaultsLoader.parseValue( "dummy", "false", boolean.class ) );
+ assertEquals( true, UIDefaultsLoader.parseValue( "dummy", "true", Boolean.class ) );
+
+ assertEquals( "hello", UIDefaultsLoader.parseValue( "dummy", "hello", String.class ) );
+ assertEquals( "hello", UIDefaultsLoader.parseValue( "dummy", "\"hello\"", String.class ) );
+ assertEquals( "null", UIDefaultsLoader.parseValue( "dummy", "\"null\"", String.class ) );
+ assertEquals( null, UIDefaultsLoader.parseValue( "dummy", "null", String.class ) );
+
+ assertEquals( 'a', UIDefaultsLoader.parseValue( "dummy", "a", char.class ) );
+ assertEquals( 'a', UIDefaultsLoader.parseValue( "dummy", "a", Character.class ) );
+ assertEquals( 123, UIDefaultsLoader.parseValue( "dummy", "123", int.class ) );
+ assertEquals( 123, UIDefaultsLoader.parseValue( "dummy", "123", Integer.class ) );
+ assertEquals( 1.23f, UIDefaultsLoader.parseValue( "dummy", "1.23", float.class ) );
+ assertEquals( 1.23f, UIDefaultsLoader.parseValue( "dummy", "1.23", Float.class ) );
+
+ assertEquals( new Insets( 2,2,2,2 ), UIDefaultsLoader.parseValue( "dummy", "2,2,2,2", Insets.class ) );
+ assertEquals( new Dimension( 2,2 ), UIDefaultsLoader.parseValue( "dummy", "2,2", Dimension.class ) );
+ assertEquals( new Color( 0xff0000 ), UIDefaultsLoader.parseValue( "dummy", "#f00", Color.class ) );
+ }
+}
diff --git a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableInfo.java b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableInfo.java
new file mode 100644
index 00000000..24a0545a
--- /dev/null
+++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableInfo.java
@@ -0,0 +1,1126 @@
+/*
+ * Copyright 2021 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.ui;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static com.formdev.flatlaf.ui.TestUtils.assertMapEquals;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import javax.swing.*;
+import javax.swing.table.JTableHeader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import com.formdev.flatlaf.icons.*;
+
+/**
+ * @author Karl Tauber
+ */
+public class TestFlatStyleableInfo
+{
+ @BeforeAll
+ static void setup() {
+ TestUtils.setup( false );
+ }
+
+ @AfterAll
+ static void cleanup() {
+ TestUtils.cleanup();
+ }
+
+ private Map> expectedMap( Object... keyValuePairs ) {
+ Map> map = new LinkedHashMap<>();
+ expectedMap( map, keyValuePairs );
+ return map;
+ }
+
+ private void expectedMap( Map> map, Object... keyValuePairs ) {
+ for( int i = 0; i < keyValuePairs.length; i += 2 )
+ map.put( (String) keyValuePairs[i], (Class>) keyValuePairs[i+1] );
+ }
+
+ //---- components ---------------------------------------------------------
+
+ @Test
+ void button() {
+ JButton b = new JButton();
+ FlatButtonUI ui = (FlatButtonUI) b.getUI();
+
+ Map> expected = new LinkedHashMap<>();
+ button( expected );
+
+ //---- FlatHelpButtonIcon ----
+
+ expectedMap( expected,
+ "help.focusWidth", int.class,
+ "help.focusColor", Color.class,
+ "help.innerFocusWidth", float.class,
+ "help.borderWidth", int.class,
+
+ "help.borderColor", Color.class,
+ "help.disabledBorderColor", Color.class,
+ "help.focusedBorderColor", Color.class,
+ "help.hoverBorderColor", Color.class,
+ "help.background", Color.class,
+ "help.disabledBackground", Color.class,
+ "help.focusedBackground", Color.class,
+ "help.hoverBackground", Color.class,
+ "help.pressedBackground", Color.class,
+ "help.questionMarkColor", Color.class,
+ "help.disabledQuestionMarkColor", Color.class
+ );
+
+ assertMapEquals( expected, ui.getStyleableInfos( b ) );
+ }
+
+ private void button( Map> expected ) {
+ expectedMap( expected,
+ "minimumWidth", int.class,
+
+ "focusedBackground", Color.class,
+ "hoverBackground", Color.class,
+ "pressedBackground", Color.class,
+ "selectedBackground", Color.class,
+ "selectedForeground", Color.class,
+ "disabledBackground", Color.class,
+ "disabledText", Color.class,
+ "disabledSelectedBackground", Color.class,
+
+ "default.background", Color.class,
+ "default.foreground", Color.class,
+ "default.focusedBackground", Color.class,
+ "default.hoverBackground", Color.class,
+ "default.pressedBackground", Color.class,
+ "default.boldText", boolean.class,
+
+ "paintShadow", boolean.class,
+ "shadowWidth", int.class,
+ "shadowColor", Color.class,
+ "default.shadowColor", Color.class,
+
+ "toolbar.spacingInsets", Insets.class,
+ "toolbar.hoverBackground", Color.class,
+ "toolbar.pressedBackground", Color.class,
+ "toolbar.selectedBackground", Color.class
+ );
+
+ // border
+ flatButtonBorder( expected );
+ }
+
+ @Test
+ void checkBox() {
+ JCheckBox c = new JCheckBox();
+ FlatCheckBoxUI ui = (FlatCheckBoxUI) c.getUI();
+
+ assertTrue( ui.getDefaultIcon() instanceof FlatCheckBoxIcon );
+
+ // FlatCheckBoxUI extends FlatRadioButtonUI
+ Map> expected = new LinkedHashMap<>();
+ radioButton( expected );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void comboBox() {
+ JComboBox c = new JComboBox<>();
+ FlatComboBoxUI ui = (FlatComboBoxUI) c.getUI();
+
+ Map> expected = expectedMap(
+ "padding", Insets.class,
+
+ "minimumWidth", int.class,
+ "editorColumns", int.class,
+ "buttonStyle", String.class,
+ "arrowType", String.class,
+ "borderColor", Color.class,
+ "disabledBorderColor", Color.class,
+
+ "editableBackground", Color.class,
+ "focusedBackground", Color.class,
+ "disabledBackground", Color.class,
+ "disabledForeground", Color.class,
+
+ "buttonBackground", Color.class,
+ "buttonFocusedBackground", Color.class,
+ "buttonEditableBackground", Color.class,
+ "buttonArrowColor", Color.class,
+ "buttonDisabledArrowColor", Color.class,
+ "buttonHoverArrowColor", Color.class,
+ "buttonPressedArrowColor", Color.class,
+
+ "popupBackground", Color.class
+ );
+
+ // border
+ flatRoundBorder( expected );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void editorPane() {
+ JEditorPane c = new JEditorPane();
+ FlatEditorPaneUI ui = (FlatEditorPaneUI) c.getUI();
+
+ Map> expected = expectedMap(
+ "minimumWidth", int.class,
+ "disabledBackground", Color.class,
+ "inactiveBackground", Color.class,
+ "focusedBackground", Color.class
+ );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void formattedTextField() {
+ JFormattedTextField c = new JFormattedTextField();
+ FlatFormattedTextFieldUI ui = (FlatFormattedTextFieldUI) c.getUI();
+
+ // FlatFormattedTextFieldUI extends FlatTextFieldUI
+ Map> expected = new LinkedHashMap<>();
+ textField( expected );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void internalFrame() {
+ JInternalFrame c = new JInternalFrame();
+ FlatInternalFrameUI ui = (FlatInternalFrameUI) c.getUI();
+
+ Map> expected = expectedMap(
+ "activeBorderColor", Color.class,
+ "inactiveBorderColor", Color.class,
+ "borderLineWidth", int.class,
+ "dropShadowPainted", boolean.class,
+ "borderMargins", Insets.class,
+
+ "activeDropShadowColor", Color.class,
+ "activeDropShadowInsets", Insets.class,
+ "activeDropShadowOpacity", float.class,
+ "inactiveDropShadowColor", Color.class,
+ "inactiveDropShadowInsets", Insets.class,
+ "inactiveDropShadowOpacity", float.class
+ );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void label() {
+ JLabel c = new JLabel();
+ FlatLabelUI ui = (FlatLabelUI) c.getUI();
+
+ Map> expected = expectedMap(
+ "disabledForeground", Color.class
+ );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void list() {
+ JList c = new JList<>();
+ FlatListUI ui = (FlatListUI) c.getUI();
+
+ Map> expected = expectedMap(
+ "selectionBackground", Color.class,
+ "selectionForeground", Color.class,
+ "selectionInactiveBackground", Color.class,
+ "selectionInactiveForeground", Color.class,
+
+ // FlatListCellBorder
+ "cellMargins", Insets.class,
+ "cellFocusColor", Color.class,
+ "showCellFocusIndicator", boolean.class
+ );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void menuBar() {
+ JMenuBar c = new JMenuBar();
+ FlatMenuBarUI ui = (FlatMenuBarUI) c.getUI();
+
+ Map> expected = expectedMap(
+ "itemMargins", Insets.class,
+ "hoverBackground", Color.class,
+ "underlineSelectionBackground", Color.class,
+ "underlineSelectionColor", Color.class,
+ "underlineSelectionHeight", int.class,
+
+ // FlatMenuBarBorder
+ "borderColor", Color.class
+ );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void menu() {
+ JMenu c = new JMenu();
+ FlatMenuUI ui = (FlatMenuUI) c.getUI();
+
+ Map> expected = new LinkedHashMap<>();
+ menuItem( expected );
+ menuItem_arrowIcon( expected );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void menuItem() {
+ JMenuItem c = new JMenuItem();
+ FlatMenuItemUI ui = (FlatMenuItemUI) c.getUI();
+
+ Map> expected = new LinkedHashMap<>();
+ menuItem( expected );
+ menuItem_arrowIcon( expected );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void checkBoxMenuItem() {
+ JCheckBoxMenuItem c = new JCheckBoxMenuItem();
+ FlatCheckBoxMenuItemUI ui = (FlatCheckBoxMenuItemUI) c.getUI();
+
+ Map> expected = new LinkedHashMap<>();
+ menuItem( expected );
+ menuItem_checkIcon( expected );
+ menuItem_arrowIcon( expected );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void radioButtonMenuItem() {
+ JRadioButtonMenuItem c = new JRadioButtonMenuItem();
+ FlatRadioButtonMenuItemUI ui = (FlatRadioButtonMenuItemUI) c.getUI();
+
+ Map> expected = new LinkedHashMap<>();
+ menuItem( expected );
+ menuItem_checkIcon( expected );
+ menuItem_arrowIcon( expected );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ private void menuItem( Map> expected ) {
+ expectedMap( expected,
+ "selectionBackground", Color.class,
+ "selectionForeground", Color.class,
+ "disabledForeground", Color.class,
+ "acceleratorForeground", Color.class,
+ "acceleratorSelectionForeground", Color.class
+ );
+
+ menuItemRenderer( expected );
+ }
+
+ private void menuItemRenderer( Map> expected ) {
+ expectedMap( expected,
+ "minimumWidth", int.class,
+ "minimumIconSize", Dimension.class,
+ "textAcceleratorGap", int.class,
+ "textNoAcceleratorGap", int.class,
+ "acceleratorArrowGap", int.class,
+
+ "checkBackground", Color.class,
+ "checkMargins", Insets.class,
+
+ "underlineSelectionBackground", Color.class,
+ "underlineSelectionCheckBackground", Color.class,
+ "underlineSelectionColor", Color.class,
+ "underlineSelectionHeight", int.class
+ );
+ }
+
+ private void menuItem_checkIcon( Map> expected ) {
+ expectedMap( expected,
+ "icon.checkmarkColor", Color.class,
+ "icon.disabledCheckmarkColor", Color.class,
+ "selectionForeground", Color.class
+ );
+ }
+
+ private void menuItem_arrowIcon( Map> expected ) {
+ expectedMap( expected,
+ "icon.arrowType", String.class,
+ "icon.arrowColor", Color.class,
+ "icon.disabledArrowColor", Color.class,
+ "selectionForeground", Color.class
+ );
+ }
+
+ @Test
+ void passwordField() {
+ JPasswordField c = new JPasswordField();
+ FlatPasswordFieldUI ui = (FlatPasswordFieldUI) c.getUI();
+
+ Map> expected = new LinkedHashMap<>();
+ expectedMap( expected,
+ "showCapsLock", boolean.class
+ );
+
+ // FlatPasswordFieldUI extends FlatTextFieldUI
+ textField( expected );
+
+ expectedMap( expected,
+ // capsLockIcon
+ "capsLockIconColor", Color.class
+ );
+
+ // border
+ flatTextBorder( expected );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void popupMenu() {
+ JPopupMenu c = new JPopupMenu();
+ FlatPopupMenuUI ui = (FlatPopupMenuUI) c.getUI();
+
+ Map> expected = expectedMap(
+ "borderInsets", Insets.class,
+ "borderColor", Color.class
+ );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void popupMenuSeparator() {
+ JPopupMenu.Separator c = new JPopupMenu.Separator();
+ FlatPopupMenuSeparatorUI ui = (FlatPopupMenuSeparatorUI) c.getUI();
+
+ Map> expected = new LinkedHashMap<>();
+
+ // FlatPopupMenuSeparatorUI extends FlatSeparatorUI
+ separator( expected );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void progressBar() {
+ JProgressBar c = new JProgressBar();
+ FlatProgressBarUI ui = (FlatProgressBarUI) c.getUI();
+
+ Map> expected = expectedMap(
+ "arc", int.class,
+ "horizontalSize", Dimension.class,
+ "verticalSize", Dimension.class
+ );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void radioButton() {
+ JRadioButton c = new JRadioButton();
+ FlatRadioButtonUI ui = (FlatRadioButtonUI) c.getUI();
+
+ assertTrue( ui.getDefaultIcon() instanceof FlatRadioButtonIcon );
+
+ Map> expected = new LinkedHashMap<>();
+ radioButton( expected );
+
+ expectedMap( expected,
+ "icon.centerDiameter", int.class
+ );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ private void radioButton( Map> expected ) {
+ expectedMap( expected,
+ "disabledText", Color.class,
+
+ //---- icon ----
+
+ "icon.focusWidth", int.class,
+ "icon.focusColor", Color.class,
+ "icon.arc", int.class,
+
+ // enabled
+ "icon.borderColor", Color.class,
+ "icon.background", Color.class,
+ "icon.selectedBorderColor", Color.class,
+ "icon.selectedBackground", Color.class,
+ "icon.checkmarkColor", Color.class,
+
+ // disabled
+ "icon.disabledBorderColor", Color.class,
+ "icon.disabledBackground", Color.class,
+ "icon.disabledCheckmarkColor", Color.class,
+
+ // focused
+ "icon.focusedBorderColor", Color.class,
+ "icon.focusedBackground", Color.class,
+ "icon.selectedFocusedBorderColor", Color.class,
+ "icon.selectedFocusedBackground", Color.class,
+ "icon.selectedFocusedCheckmarkColor", Color.class,
+
+ // hover
+ "icon.hoverBorderColor", Color.class,
+ "icon.hoverBackground", Color.class,
+ "icon.selectedHoverBackground", Color.class,
+
+ // pressed
+ "icon.pressedBackground", Color.class,
+ "icon.selectedPressedBackground", Color.class
+ );
+ }
+
+ @Test
+ void scrollBar() {
+ JScrollBar c = new JScrollBar();
+ FlatScrollBarUI ui = (FlatScrollBarUI) c.getUI();
+
+ Map> expected = expectedMap(
+ "track", Color.class,
+ "thumb", Color.class,
+ "width", int.class,
+ "minimumThumbSize", Dimension.class,
+ "maximumThumbSize", Dimension.class,
+ "allowsAbsolutePositioning", boolean.class,
+
+ "trackInsets", Insets.class,
+ "thumbInsets", Insets.class,
+ "trackArc", int.class,
+ "thumbArc", int.class,
+ "hoverTrackColor", Color.class,
+ "hoverThumbColor", Color.class,
+ "hoverThumbWithTrack", boolean.class,
+ "pressedTrackColor", Color.class,
+ "pressedThumbColor", Color.class,
+ "pressedThumbWithTrack", boolean.class,
+
+ "showButtons", boolean.class,
+ "arrowType", String.class,
+ "buttonArrowColor", Color.class,
+ "buttonDisabledArrowColor", Color.class,
+ "hoverButtonBackground", Color.class,
+ "pressedButtonBackground", Color.class
+ );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void scrollPane() {
+ JScrollPane c = new JScrollPane();
+ FlatScrollPaneUI ui = (FlatScrollPaneUI) c.getUI();
+
+ Map> expected = new LinkedHashMap<>();
+
+ // border
+ flatBorder( expected );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void separator() {
+ JSeparator c = new JSeparator();
+ FlatSeparatorUI ui = (FlatSeparatorUI) c.getUI();
+
+ Map> expected = new LinkedHashMap<>();
+ separator( expected );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ private void separator( Map> expected ) {
+ expectedMap( expected,
+ "height", int.class,
+ "stripeWidth", int.class,
+ "stripeIndent", int.class
+ );
+ }
+
+ @Test
+ void slider() {
+ JSlider c = new JSlider();
+ FlatSliderUI ui = (FlatSliderUI) c.getUI();
+
+ Map> expected = expectedMap(
+ "trackWidth", int.class,
+ "thumbSize", Dimension.class,
+ "focusWidth", int.class,
+
+ "trackValueColor", Color.class,
+ "trackColor", Color.class,
+ "thumbColor", Color.class,
+ "thumbBorderColor", Color.class,
+ "focusedColor", Color.class,
+ "focusedThumbBorderColor", Color.class,
+ "hoverThumbColor", Color.class,
+ "pressedThumbColor", Color.class,
+ "disabledTrackColor", Color.class,
+ "disabledThumbColor", Color.class,
+ "disabledThumbBorderColor", Color.class,
+ "tickColor", Color.class
+ );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void spinner() {
+ JSpinner c = new JSpinner();
+ FlatSpinnerUI ui = (FlatSpinnerUI) c.getUI();
+
+ Map> expected = expectedMap(
+ "minimumWidth", int.class,
+ "buttonStyle", String.class,
+ "arrowType", String.class,
+ "borderColor", Color.class,
+ "disabledBorderColor", Color.class,
+ "disabledBackground", Color.class,
+ "disabledForeground", Color.class,
+ "focusedBackground", Color.class,
+ "buttonBackground", Color.class,
+ "buttonArrowColor", Color.class,
+ "buttonDisabledArrowColor", Color.class,
+ "buttonHoverArrowColor", Color.class,
+ "buttonPressedArrowColor", Color.class,
+ "padding", Insets.class
+ );
+
+ // border
+ flatRoundBorder( expected );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void splitPane() {
+ JSplitPane c = new JSplitPane();
+ FlatSplitPaneUI ui = (FlatSplitPaneUI) c.getUI();
+
+ Map> expected = expectedMap(
+ "arrowType", String.class,
+ "oneTouchArrowColor", Color.class,
+ "oneTouchHoverArrowColor", Color.class,
+ "oneTouchPressedArrowColor", Color.class,
+
+ "style", String.class,
+ "gripColor", Color.class,
+ "gripDotCount", int.class,
+ "gripDotSize", int.class,
+ "gripGap", int.class
+ );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void tabbedPane() {
+ JTabbedPane c = new JTabbedPane();
+ FlatTabbedPaneUI ui = (FlatTabbedPaneUI) c.getUI();
+
+ Map> expected = expectedMap(
+ "tabInsets", Insets.class,
+ "tabAreaInsets", Insets.class,
+ "textIconGap", int.class,
+
+ "disabledForeground", Color.class,
+
+ "selectedBackground", Color.class,
+ "selectedForeground", Color.class,
+ "underlineColor", Color.class,
+ "disabledUnderlineColor", Color.class,
+ "hoverColor", Color.class,
+ "focusColor", Color.class,
+ "tabSeparatorColor", Color.class,
+ "contentAreaColor", Color.class,
+
+ "minimumTabWidth", int.class,
+ "maximumTabWidth", int.class,
+ "tabHeight", int.class,
+ "tabSelectionHeight", int.class,
+ "contentSeparatorHeight", int.class,
+ "showTabSeparators", boolean.class,
+ "tabSeparatorsFullHeight", boolean.class,
+ "hasFullBorder", boolean.class,
+ "tabsOpaque", boolean.class,
+
+ "tabsPopupPolicy", String.class,
+ "scrollButtonsPolicy", String.class,
+ "scrollButtonsPlacement", String.class,
+
+ "tabAreaAlignment", String.class,
+ "tabAlignment", String.class,
+ "tabWidthMode", String.class,
+
+ "arrowType", String.class,
+ "buttonInsets", Insets.class,
+ "buttonArc", int.class,
+ "buttonHoverBackground", Color.class,
+ "buttonPressedBackground", Color.class,
+
+ "moreTabsButtonToolTipText", String.class,
+
+ // FlatTabbedPaneCloseIcon
+ "closeSize", Dimension.class,
+ "closeArc", int.class,
+ "closeCrossPlainSize", float.class,
+ "closeCrossFilledSize", float.class,
+ "closeCrossLineWidth", float.class,
+ "closeBackground", Color.class,
+ "closeForeground", Color.class,
+ "closeHoverBackground", Color.class,
+ "closeHoverForeground", Color.class,
+ "closePressedBackground", Color.class,
+ "closePressedForeground", Color.class
+ );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void table() {
+ JTable c = new JTable();
+ FlatTableUI ui = (FlatTableUI) c.getUI();
+
+ Map> expected = expectedMap(
+ "showTrailingVerticalLine", boolean.class,
+ "selectionBackground", Color.class,
+ "selectionForeground", Color.class,
+ "selectionInactiveBackground", Color.class,
+ "selectionInactiveForeground", Color.class,
+
+ // FlatTableCellBorder
+ "cellMargins", Insets.class,
+ "cellFocusColor", Color.class,
+ "showCellFocusIndicator", boolean.class
+ );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void tableHeader() {
+ JTableHeader c = new JTableHeader();
+ FlatTableHeaderUI ui = (FlatTableHeaderUI) c.getUI();
+
+ Map> expected = expectedMap(
+ "bottomSeparatorColor", Color.class,
+ "height", int.class,
+ "sortIconPosition", String.class,
+
+ // FlatTableHeaderBorder
+ "cellMargins", Insets.class,
+ "separatorColor", Color.class,
+ "showTrailingVerticalLine", boolean.class,
+
+ // FlatAscendingSortIcon and FlatDescendingSortIcon
+ "arrowType", String.class,
+ "sortIconColor", Color.class
+ );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void textArea() {
+ JTextArea c = new JTextArea();
+ FlatTextAreaUI ui = (FlatTextAreaUI) c.getUI();
+
+ Map> expected = expectedMap(
+ "minimumWidth", int.class,
+ "disabledBackground", Color.class,
+ "inactiveBackground", Color.class,
+ "focusedBackground", Color.class
+ );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void textField() {
+ JTextField c = new JTextField();
+ FlatTextFieldUI ui = (FlatTextFieldUI) c.getUI();
+
+ Map> expected = new LinkedHashMap<>();
+ textField( expected );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ private void textField( Map> expected ) {
+ expectedMap( expected,
+ "minimumWidth", int.class,
+ "disabledBackground", Color.class,
+ "inactiveBackground", Color.class,
+ "placeholderForeground", Color.class,
+ "focusedBackground", Color.class
+ );
+
+ // border
+ flatTextBorder( expected );
+ }
+
+ @Test
+ void textPane() {
+ JTextPane c = new JTextPane();
+ FlatTextPaneUI ui = (FlatTextPaneUI) c.getUI();
+
+ Map> expected = expectedMap(
+ "minimumWidth", int.class,
+ "disabledBackground", Color.class,
+ "inactiveBackground", Color.class,
+ "focusedBackground", Color.class
+ );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void toggleButton() {
+ JToggleButton b = new JToggleButton();
+ FlatToggleButtonUI ui = (FlatToggleButtonUI) b.getUI();
+
+ Map> expected = expectedMap(
+ "tab.underlineHeight", int.class,
+ "tab.underlineColor", Color.class,
+ "tab.disabledUnderlineColor", Color.class,
+ "tab.selectedBackground", Color.class,
+ "tab.hoverBackground", Color.class,
+ "tab.focusBackground", Color.class
+ );
+
+ // FlatToggleButtonUI extends FlatButtonUI
+ button( expected );
+
+ assertMapEquals( expected, ui.getStyleableInfos( b ) );
+ }
+
+ @Test
+ void toolBar() {
+ JToolBar c = new JToolBar();
+ FlatToolBarUI ui = (FlatToolBarUI) c.getUI();
+
+ Map> expected = expectedMap(
+ "focusableButtons", boolean.class,
+
+ "borderMargins", Insets.class,
+ "gripColor", Color.class
+ );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void toolBarSeparator() {
+ JToolBar.Separator c = new JToolBar.Separator();
+ FlatToolBarSeparatorUI ui = (FlatToolBarSeparatorUI) c.getUI();
+
+ Map> expected = expectedMap(
+ "separatorWidth", int.class,
+ "separatorColor", Color.class
+ );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ @Test
+ void tree() {
+ JTree c = new JTree();
+ FlatTreeUI ui = (FlatTreeUI) c.getUI();
+
+ Map> expected = expectedMap(
+ "selectionBackground", Color.class,
+ "selectionForeground", Color.class,
+ "selectionInactiveBackground", Color.class,
+ "selectionInactiveForeground", Color.class,
+ "selectionBorderColor", Color.class,
+ "wideSelection", boolean.class,
+ "showCellFocusIndicator", boolean.class,
+
+ // icons
+ "icon.arrowType", String.class,
+ "icon.expandedColor", Color.class,
+ "icon.collapsedColor", Color.class,
+ "icon.leafColor", Color.class,
+ "icon.closedColor", Color.class,
+ "icon.openColor", Color.class
+ );
+
+ assertMapEquals( expected, ui.getStyleableInfos( c ) );
+ }
+
+ //---- component borders --------------------------------------------------
+
+ private void flatButtonBorder( Map> expected ) {
+ flatBorder( expected );
+
+ expectedMap( expected,
+ "borderColor", Color.class,
+ "disabledBorderColor", Color.class,
+ "focusedBorderColor", Color.class,
+ "hoverBorderColor", Color.class,
+
+ "default.borderColor", Color.class,
+ "default.focusedBorderColor", Color.class,
+ "default.focusColor", Color.class,
+ "default.hoverBorderColor", Color.class,
+ "toolbar.focusColor", Color.class,
+
+ "borderWidth", int.class,
+ "default.borderWidth", int.class,
+ "toolbar.margin", Insets.class,
+ "toolbar.spacingInsets", Insets.class,
+ "toolbar.focusWidth", float.class,
+ "arc", int.class
+ );
+ }
+
+ private void flatRoundBorder( Map> expected ) {
+ flatBorder( expected );
+
+ expectedMap( expected,
+ "arc", int.class
+ );
+ }
+
+ private void flatTextBorder( Map> expected ) {
+ flatBorder( expected );
+
+ expectedMap( expected,
+ "arc", int.class
+ );
+ }
+
+ private void flatBorder( Map> expected ) {
+ expectedMap( expected,
+ "focusWidth", int.class,
+ "innerFocusWidth", float.class,
+ "innerOutlineWidth", float.class,
+ "focusColor", Color.class,
+ "borderColor", Color.class,
+ "disabledBorderColor", Color.class,
+ "focusedBorderColor", Color.class,
+
+ "error.borderColor", Color.class,
+ "error.focusedBorderColor", Color.class,
+ "warning.borderColor", Color.class,
+ "warning.focusedBorderColor", Color.class,
+ "custom.borderColor", Color.class
+ );
+ }
+
+ //---- borders ------------------------------------------------------------
+
+ @Test
+ void flatButtonBorder() {
+ FlatButtonBorder border = new FlatButtonBorder();
+
+ Map> expected = new LinkedHashMap<>();
+ flatButtonBorder( expected );
+
+ assertMapEquals( expected, border.getStyleableInfos() );
+ }
+
+ @Test
+ void flatRoundBorder() {
+ FlatRoundBorder border = new FlatRoundBorder();
+
+ Map> expected = new LinkedHashMap<>();
+ flatRoundBorder( expected );
+
+ assertMapEquals( expected, border.getStyleableInfos() );
+ }
+
+ @Test
+ void flatTextBorder() {
+ FlatTextBorder border = new FlatTextBorder();
+
+ Map> expected = new LinkedHashMap<>();
+ flatTextBorder( expected );
+
+ assertMapEquals( expected, border.getStyleableInfos() );
+ }
+
+ @Test
+ void flatBorder() {
+ FlatBorder border = new FlatBorder();
+
+ Map> expected = new LinkedHashMap<>();
+ flatBorder( expected );
+
+ assertMapEquals( expected, border.getStyleableInfos() );
+ }
+
+ //---- icons --------------------------------------------------------------
+
+ @Test
+ void flatCheckBoxIcon() {
+ FlatCheckBoxIcon icon = new FlatCheckBoxIcon();
+
+ Map> expected = new LinkedHashMap<>();
+ flatCheckBoxIcon( expected );
+
+ assertMapEquals( expected, icon.getStyleableInfos() );
+ }
+
+ @Test
+ void flatRadioButtonIcon() {
+ FlatRadioButtonIcon icon = new FlatRadioButtonIcon();
+
+ // FlatRadioButtonIcon extends FlatCheckBoxIcon
+ Map> expected = new LinkedHashMap<>();
+ flatCheckBoxIcon( expected );
+
+ expectedMap( expected,
+ "centerDiameter", int.class
+ );
+
+ assertMapEquals( expected, icon.getStyleableInfos() );
+ }
+
+ private void flatCheckBoxIcon( Map> expected ) {
+ expectedMap( expected,
+ "focusWidth", int.class,
+ "focusColor", Color.class,
+ "arc", int.class,
+
+ // enabled
+ "borderColor", Color.class,
+ "background", Color.class,
+ "selectedBorderColor", Color.class,
+ "selectedBackground", Color.class,
+ "checkmarkColor", Color.class,
+
+ // disabled
+ "disabledBorderColor", Color.class,
+ "disabledBackground", Color.class,
+ "disabledCheckmarkColor", Color.class,
+
+ // focused
+ "focusedBorderColor", Color.class,
+ "focusedBackground", Color.class,
+ "selectedFocusedBorderColor", Color.class,
+ "selectedFocusedBackground", Color.class,
+ "selectedFocusedCheckmarkColor", Color.class,
+
+ // hover
+ "hoverBorderColor", Color.class,
+ "hoverBackground", Color.class,
+ "selectedHoverBackground", Color.class,
+
+ // pressed
+ "pressedBackground", Color.class,
+ "selectedPressedBackground", Color.class
+ );
+ }
+
+ @Test
+ void flatCheckBoxMenuItemIcon() {
+ FlatCheckBoxMenuItemIcon icon = new FlatCheckBoxMenuItemIcon();
+
+ Map> expected = new LinkedHashMap<>();
+ flatCheckBoxMenuItemIcon( expected );
+
+ assertMapEquals( expected, icon.getStyleableInfos() );
+ }
+
+ @Test
+ void flatRadioButtonMenuItemIcon() {
+ FlatRadioButtonMenuItemIcon icon = new FlatRadioButtonMenuItemIcon();
+
+ // FlatRadioButtonMenuItemIcon extends FlatCheckBoxMenuItemIcon
+ Map> expected = new LinkedHashMap<>();
+ flatCheckBoxMenuItemIcon( expected );
+
+ assertMapEquals( expected, icon.getStyleableInfos() );
+ }
+
+ private void flatCheckBoxMenuItemIcon( Map> expected ) {
+ expectedMap( expected,
+ "checkmarkColor", Color.class,
+ "disabledCheckmarkColor", Color.class,
+ "selectionForeground", Color.class
+ );
+ }
+
+ @Test
+ void flatMenuArrowIcon() {
+ FlatMenuArrowIcon icon = new FlatMenuArrowIcon();
+
+ Map> expected = new LinkedHashMap<>();
+ flatMenuArrowIcon( expected );
+
+ assertMapEquals( expected, icon.getStyleableInfos() );
+ }
+
+ @Test
+ void flatMenuItemArrowIcon() {
+ FlatMenuItemArrowIcon icon = new FlatMenuItemArrowIcon();
+
+ // FlatMenuItemArrowIcon extends FlatMenuArrowIcon
+ Map> expected = new LinkedHashMap<>();
+ flatMenuArrowIcon( expected );
+
+ assertMapEquals( expected, icon.getStyleableInfos() );
+ }
+
+ private void flatMenuArrowIcon( Map> expected ) {
+ expectedMap( expected,
+ "arrowType", String.class,
+ "arrowColor", Color.class,
+ "disabledArrowColor", Color.class,
+ "selectionForeground", Color.class
+ );
+ }
+
+ @Test
+ void flatHelpButtonIcon() {
+ FlatHelpButtonIcon icon = new FlatHelpButtonIcon();
+
+ Map> expected = expectedMap(
+ "focusWidth", int.class,
+ "focusColor", Color.class,
+ "innerFocusWidth", float.class,
+ "borderWidth", int.class,
+
+ "borderColor", Color.class,
+ "disabledBorderColor", Color.class,
+ "focusedBorderColor", Color.class,
+ "hoverBorderColor", Color.class,
+ "background", Color.class,
+ "disabledBackground", Color.class,
+ "focusedBackground", Color.class,
+ "hoverBackground", Color.class,
+ "pressedBackground", Color.class,
+ "questionMarkColor", Color.class,
+ "disabledQuestionMarkColor", Color.class
+ );
+
+ assertMapEquals( expected, icon.getStyleableInfos() );
+ }
+}
diff --git a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyling.java b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyling.java
new file mode 100644
index 00000000..8f458c99
--- /dev/null
+++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyling.java
@@ -0,0 +1,1281 @@
+/*
+ * Copyright 2021 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.ui;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+import javax.swing.*;
+import javax.swing.table.JTableHeader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import com.formdev.flatlaf.icons.*;
+
+/**
+ * @author Karl Tauber
+ */
+public class TestFlatStyling
+{
+ @BeforeAll
+ static void setup() {
+ TestUtils.setup( false );
+ }
+
+ @AfterAll
+ static void cleanup() {
+ TestUtils.cleanup();
+ }
+
+ @Test
+ void parse() {
+ assertEquals( null, FlatStylingSupport.parse( null ) );
+ assertEquals( null, FlatStylingSupport.parse( "" ) );
+ assertEquals( null, FlatStylingSupport.parse( " " ) );
+ assertEquals( null, FlatStylingSupport.parse( ";" ) );
+ assertEquals( null, FlatStylingSupport.parse( " ; ; " ) );
+
+ assertEquals(
+ expectedMap( "background", Color.WHITE ),
+ FlatStylingSupport.parse( "background: #fff" ) );
+ assertEquals(
+ expectedMap( "background", Color.WHITE, "foreground", Color.BLACK ),
+ FlatStylingSupport.parse( "background: #fff; foreground: #000" ) );
+ assertEquals(
+ expectedMap( "background", Color.WHITE, "foreground", Color.BLACK, "someWidth", 20 ),
+ FlatStylingSupport.parse( "background: #fff; foreground: #000; someWidth: 20" ) );
+ }
+
+ @Test
+ void parseColorFunctions() {
+ testColorStyle( 0x0c2238, "rgb(12,34,56)" );
+ testColorStyle( 0x4e0c2238, "rgba(12,34,56,78)" );
+ testColorStyle( 0xb57869, "hsl(12,34%,56%)" );
+ testColorStyle( 0xc7b57869, "hsla(12,34%,56%,78%)" );
+
+ testColorStyle( 0xff6666, "lighten(#f00,20%)" );
+ testColorStyle( 0x990000, "darken(#f00,20%)" );
+
+ testColorStyle( 0x9c3030, "saturate(#844,20%)" );
+ testColorStyle( 0x745858, "desaturate(#844,20%)" );
+
+ testColorStyle( 0x4dff0000, "fadein(#ff000000,30%)" );
+ testColorStyle( 0x99ff0000, "fadeout(#ff0000,40%)" );
+ testColorStyle( 0x80ff0000, "fade(#ff0000,50%)" );
+
+ testColorStyle( 0xffaa00, "spin(#f00,40)" );
+ testColorStyle( 0xff00aa, "spin(#f00,-40)" );
+
+ testColorStyle( 0x00ffff, "changeHue(#f00,180)" );
+ testColorStyle( 0xbf4040, "changeSaturation(#f00,50%)" );
+ testColorStyle( 0xff9999, "changeLightness(#f00,80%)" );
+ testColorStyle( 0x80ff0000, "changeAlpha(#f00,50%)" );
+
+ testColorStyle( 0x1ae600, "mix(#f00,#0f0,10%)" );
+ testColorStyle( 0x40bf00, "mix(#f00,#0f0,25%)" );
+ testColorStyle( 0x808000, "mix(#f00,#0f0)" );
+ testColorStyle( 0xbf4000, "mix(#f00,#0f0,75%)" );
+ testColorStyle( 0xe61a00, "mix(#f00,#0f0,90%)" );
+
+ testColorStyle( 0xff40ff, "tint(#f0f,25%)" );
+ testColorStyle( 0xff80ff, "tint(#f0f)" );
+ testColorStyle( 0xffbfff, "tint(#f0f,75%)" );
+
+ testColorStyle( 0xbf00bf, "shade(#f0f,25%)" );
+ testColorStyle( 0x800080, "shade(#f0f)" );
+ testColorStyle( 0x400040, "shade(#f0f,75%)" );
+
+ // nested
+ testColorStyle( 0xd1c7c7, "saturate(darken(#fff,20%),10%)" );
+ testColorStyle( 0xcf00cf, "shade(shade(#f0f,10%),10%)" );
+ testColorStyle( 0xba00ba, "shade(shade(shade(#f0f,10%),10%),10%)" );
+ }
+
+ @Test
+ void parseReferences() {
+ assertEquals( Color.white, UIManager.getColor( "TextField.background" ) );
+
+ testColorStyle( 0xffffff, "$TextField.background" );
+ testColorStyle( 0xcccccc, "darken($TextField.background,20%)" );
+ testColorStyle( 0xd1c7c7, "saturate(darken($TextField.background,20%),10%)" );
+
+ testStyle( "hideMnemonics", true, "$Component.hideMnemonics" );
+ testStyle( "arc", 6, "$Button.arc" );
+ testStyle( "dropShadowOpacity", 0.15f, "$Popup.dropShadowOpacity" );
+ testStyle( "margin", new Insets( 2, 14, 2, 14 ) , "$Button.margin" );
+ testStyle( "iconSize", new Dimension( 64, 64 ), "$DesktopIcon.iconSize" );
+ testStyle( "arrowType", "chevron", "$Component.arrowType" );
+ }
+
+ private void testColorStyle( int expectedRGB, String style ) {
+ testStyle( "background", new Color( expectedRGB, (expectedRGB & 0xff000000) != 0 ), style );
+ }
+
+ private void testStyle( String key, Object expected, String style ) {
+ assertEquals(
+ expectedMap( key, expected ),
+ FlatStylingSupport.parse( key + ": " + style ) );
+ }
+
+ private Map expectedMap( Object... keyValuePairs ) {
+ Map map = new HashMap<>();
+ for( int i = 0; i < keyValuePairs.length; i += 2 )
+ map.put( keyValuePairs[i], keyValuePairs[i+1] );
+ return map;
+ }
+
+ //---- components ---------------------------------------------------------
+
+ @Test
+ void button() {
+ JButton b = new JButton();
+ FlatButtonUI ui = (FlatButtonUI) b.getUI();
+
+ button( b, ui );
+
+ //---- FlatHelpButtonIcon ----
+
+ ui.applyStyle( b, "help.focusWidth: 2" );
+ ui.applyStyle( b, "help.focusColor: #fff" );
+ ui.applyStyle( b, "help.innerFocusWidth: {float}0.5" );
+ ui.applyStyle( b, "help.borderWidth: 1" );
+
+ ui.applyStyle( b, "help.borderColor: #fff" );
+ ui.applyStyle( b, "help.disabledBorderColor: #fff" );
+ ui.applyStyle( b, "help.focusedBorderColor: #fff" );
+ ui.applyStyle( b, "help.hoverBorderColor: #fff" );
+ ui.applyStyle( b, "help.background: #fff" );
+ ui.applyStyle( b, "help.disabledBackground: #fff" );
+ ui.applyStyle( b, "help.focusedBackground: #fff" );
+ ui.applyStyle( b, "help.hoverBackground: #fff" );
+ ui.applyStyle( b, "help.pressedBackground: #fff" );
+ ui.applyStyle( b, "help.questionMarkColor: #fff" );
+ ui.applyStyle( b, "help.disabledQuestionMarkColor: #fff" );
+ }
+
+ private void button( AbstractButton b, FlatButtonUI ui ) {
+ ui.applyStyle( b, "minimumWidth: 100" );
+
+ ui.applyStyle( b, "focusedBackground: #fff" );
+ ui.applyStyle( b, "hoverBackground: #fff" );
+ ui.applyStyle( b, "pressedBackground: #fff" );
+ ui.applyStyle( b, "selectedBackground: #fff" );
+ ui.applyStyle( b, "selectedForeground: #fff" );
+ ui.applyStyle( b, "disabledBackground: #fff" );
+ ui.applyStyle( b, "disabledText: #fff" );
+ ui.applyStyle( b, "disabledSelectedBackground: #fff" );
+
+ ui.applyStyle( b, "default.background: #fff" );
+ ui.applyStyle( b, "default.foreground: #fff" );
+ ui.applyStyle( b, "default.focusedBackground: #fff" );
+ ui.applyStyle( b, "default.hoverBackground: #fff" );
+ ui.applyStyle( b, "default.pressedBackground: #fff" );
+ ui.applyStyle( b, "default.boldText: true" );
+
+ ui.applyStyle( b, "paintShadow: true" );
+ ui.applyStyle( b, "shadowWidth: 2" );
+ ui.applyStyle( b, "shadowColor: #fff" );
+ ui.applyStyle( b, "default.shadowColor: #fff" );
+
+ ui.applyStyle( b, "toolbar.spacingInsets: 1,2,3,4" );
+ ui.applyStyle( b, "toolbar.hoverBackground: #fff" );
+ ui.applyStyle( b, "toolbar.pressedBackground: #fff" );
+ ui.applyStyle( b, "toolbar.selectedBackground: #fff" );
+
+ // border
+ flatButtonBorder( style -> ui.applyStyle( b, style ) );
+
+ // JComponent properties
+ ui.applyStyle( b, "background: #fff" );
+ ui.applyStyle( b, "foreground: #fff" );
+ ui.applyStyle( b, "border: 2,2,2,2,#f00" );
+
+ // AbstractButton properties
+ ui.applyStyle( b, "margin: 2,2,2,2" );
+ }
+
+ @Test
+ void checkBox() {
+ JCheckBox c = new JCheckBox();
+ FlatCheckBoxUI ui = (FlatCheckBoxUI) c.getUI();
+
+ assertTrue( ui.getDefaultIcon() instanceof FlatCheckBoxIcon );
+
+ // FlatCheckBoxUI extends FlatRadioButtonUI
+ radioButton( ui, c );
+ }
+
+ @Test
+ void comboBox() {
+ JComboBox c = new JComboBox<>();
+ FlatComboBoxUI ui = (FlatComboBoxUI) c.getUI();
+
+ ui.applyStyle( "padding: 1,2,3,4" );
+
+ ui.applyStyle( "minimumWidth: 100" );
+ ui.applyStyle( "editorColumns: 10" );
+ ui.applyStyle( "buttonStyle: auto" );
+ ui.applyStyle( "arrowType: chevron" );
+ ui.applyStyle( "borderColor: #fff" );
+ ui.applyStyle( "disabledBorderColor: #fff" );
+
+ ui.applyStyle( "editableBackground: #fff" );
+ ui.applyStyle( "focusedBackground: #fff" );
+ ui.applyStyle( "disabledBackground: #fff" );
+ ui.applyStyle( "disabledForeground: #fff" );
+
+ ui.applyStyle( "buttonBackground: #fff" );
+ ui.applyStyle( "buttonFocusedBackground: #fff" );
+ ui.applyStyle( "buttonEditableBackground: #fff" );
+ ui.applyStyle( "buttonArrowColor: #fff" );
+ ui.applyStyle( "buttonDisabledArrowColor: #fff" );
+ ui.applyStyle( "buttonHoverArrowColor: #fff" );
+ ui.applyStyle( "buttonPressedArrowColor: #fff" );
+
+ ui.applyStyle( "popupBackground: #fff" );
+
+ // border
+ flatRoundBorder( style -> ui.applyStyle( style ) );
+
+ // JComponent properties
+ ui.applyStyle( "background: #fff" );
+ ui.applyStyle( "foreground: #fff" );
+ ui.applyStyle( "border: 2,2,2,2,#f00" );
+ }
+
+ @Test
+ void editorPane() {
+ JEditorPane c = new JEditorPane();
+ FlatEditorPaneUI ui = (FlatEditorPaneUI) c.getUI();
+
+ ui.applyStyle( "minimumWidth: 100" );
+ ui.applyStyle( "disabledBackground: #fff" );
+ ui.applyStyle( "inactiveBackground: #fff" );
+ ui.applyStyle( "focusedBackground: #fff" );
+
+ // JComponent properties
+ ui.applyStyle( "background: #fff" );
+ ui.applyStyle( "foreground: #fff" );
+ ui.applyStyle( "border: 2,2,2,2,#f00" );
+
+ // JTextComponent properties
+ ui.applyStyle( "caretColor: #fff" );
+ ui.applyStyle( "selectionColor: #fff" );
+ ui.applyStyle( "selectedTextColor: #fff" );
+ ui.applyStyle( "disabledTextColor: #fff" );
+ }
+
+ @Test
+ void formattedTextField() {
+ JFormattedTextField c = new JFormattedTextField();
+ FlatFormattedTextFieldUI ui = (FlatFormattedTextFieldUI) c.getUI();
+
+ // FlatFormattedTextFieldUI extends FlatTextFieldUI
+ textField( ui );
+ }
+
+ @Test
+ void internalFrame() {
+ JInternalFrame c = new JInternalFrame();
+ FlatInternalFrameUI ui = (FlatInternalFrameUI) c.getUI();
+
+ ui.applyStyle( "activeBorderColor: #fff" );
+ ui.applyStyle( "inactiveBorderColor: #fff" );
+ ui.applyStyle( "borderLineWidth: 123" );
+ ui.applyStyle( "dropShadowPainted: false" );
+ ui.applyStyle( "borderMargins: 1,2,3,4" );
+
+ ui.applyStyle( "activeDropShadowColor: #fff" );
+ ui.applyStyle( "activeDropShadowInsets: 1,2,3,4" );
+ ui.applyStyle( "activeDropShadowOpacity: 0.5" );
+ ui.applyStyle( "inactiveDropShadowColor: #fff" );
+ ui.applyStyle( "inactiveDropShadowInsets: 1,2,3,4" );
+ ui.applyStyle( "inactiveDropShadowOpacity: 0.5" );
+
+ // JComponent properties
+ ui.applyStyle( "background: #fff" );
+ ui.applyStyle( "foreground: #fff" );
+ ui.applyStyle( "border: 2,2,2,2,#f00" );
+ }
+
+ @Test
+ void label() {
+ JLabel c = new JLabel();
+ FlatLabelUI ui = (FlatLabelUI) c.getUI();
+
+ ui.applyStyle( c, "disabledForeground: #fff" );
+
+ // JComponent properties
+ ui.applyStyle( c, "background: #fff" );
+ ui.applyStyle( c, "foreground: #fff" );
+ ui.applyStyle( c, "border: 2,2,2,2,#f00" );
+
+ // JLabel properties
+ ui.applyStyle( c, "icon: com.formdev.flatlaf.icons.FlatTreeExpandedIcon" );
+ }
+
+ @Test
+ void list() {
+ JList c = new JList<>();
+ FlatListUI ui = (FlatListUI) c.getUI();
+
+ ui.applyStyle( "selectionBackground: #fff" );
+ ui.applyStyle( "selectionForeground: #fff" );
+ ui.applyStyle( "selectionInactiveBackground: #fff" );
+ ui.applyStyle( "selectionInactiveForeground: #fff" );
+
+ // FlatListCellBorder
+ ui.applyStyle( "cellMargins: 1,2,3,4" );
+ ui.applyStyle( "cellFocusColor: #fff" );
+ ui.applyStyle( "showCellFocusIndicator: true" );
+
+ // JComponent properties
+ ui.applyStyle( "background: #fff" );
+ ui.applyStyle( "foreground: #fff" );
+ ui.applyStyle( "border: 2,2,2,2,#f00" );
+
+ // JList properties
+ ui.applyStyle( "visibleRowCount: 20" );
+ }
+
+ @Test
+ void menuBar() {
+ JMenuBar c = new JMenuBar();
+ FlatMenuBarUI ui = (FlatMenuBarUI) c.getUI();
+
+ ui.applyStyle( "itemMargins: 1,2,3,4" );
+ ui.applyStyle( "hoverBackground: #fff" );
+ ui.applyStyle( "underlineSelectionBackground: #fff" );
+ ui.applyStyle( "underlineSelectionColor: #fff" );
+ ui.applyStyle( "underlineSelectionHeight: 3" );
+
+ // FlatMenuBarBorder
+ ui.applyStyle( "borderColor: #fff" );
+
+ // JComponent properties
+ ui.applyStyle( "background: #fff" );
+ ui.applyStyle( "foreground: #fff" );
+ ui.applyStyle( "border: 2,2,2,2,#f00" );
+ }
+
+ @Test
+ void menu() {
+ JMenu c = new JMenu();
+ FlatMenuUI ui = (FlatMenuUI) c.getUI();
+
+ Consumer applyStyle = style -> ui.applyStyle( style );
+ menuItem( applyStyle );
+ menuItem_arrowIcon( applyStyle );
+ }
+
+ @Test
+ void menuItem() {
+ JMenuItem c = new JMenuItem();
+ FlatMenuItemUI ui = (FlatMenuItemUI) c.getUI();
+
+ Consumer applyStyle = style -> ui.applyStyle( style );
+ menuItem( applyStyle );
+ menuItem_arrowIcon( applyStyle );
+ }
+
+ @Test
+ void checkBoxMenuItem() {
+ JCheckBoxMenuItem c = new JCheckBoxMenuItem();
+ FlatCheckBoxMenuItemUI ui = (FlatCheckBoxMenuItemUI) c.getUI();
+
+ Consumer applyStyle = style -> ui.applyStyle( style );
+ menuItem( applyStyle );
+ menuItem_checkIcon( applyStyle );
+ menuItem_arrowIcon( applyStyle );
+ }
+
+ @Test
+ void radioButtonMenuItem() {
+ JRadioButtonMenuItem c = new JRadioButtonMenuItem();
+ FlatRadioButtonMenuItemUI ui = (FlatRadioButtonMenuItemUI) c.getUI();
+
+ Consumer applyStyle = style -> ui.applyStyle( style );
+ menuItem( applyStyle );
+ menuItem_checkIcon( applyStyle );
+ menuItem_arrowIcon( applyStyle );
+ }
+
+ private void menuItem( Consumer applyStyle ) {
+ applyStyle.accept( "selectionBackground: #fff" );
+ applyStyle.accept( "selectionForeground: #fff" );
+ applyStyle.accept( "disabledForeground: #fff" );
+ applyStyle.accept( "acceleratorForeground: #fff" );
+ applyStyle.accept( "acceleratorSelectionForeground: #fff" );
+
+ menuItemRenderer( applyStyle );
+
+ // JComponent properties
+ applyStyle.accept( "background: #fff" );
+ applyStyle.accept( "foreground: #fff" );
+ applyStyle.accept( "border: 2,2,2,2,#f00" );
+
+ // AbstractButton properties
+ applyStyle.accept( "margin: 2,2,2,2" );
+ }
+
+ private void menuItemRenderer( Consumer applyStyle ) {
+ applyStyle.accept( "minimumWidth: 10" );
+ applyStyle.accept( "minimumIconSize: 16,16" );
+ applyStyle.accept( "textAcceleratorGap: 28" );
+ applyStyle.accept( "textNoAcceleratorGap: 6" );
+ applyStyle.accept( "acceleratorArrowGap: 2" );
+
+ applyStyle.accept( "checkBackground: #fff" );
+ applyStyle.accept( "checkMargins: 1,2,3,4" );
+
+ applyStyle.accept( "underlineSelectionBackground: #fff" );
+ applyStyle.accept( "underlineSelectionCheckBackground: #fff" );
+ applyStyle.accept( "underlineSelectionColor: #fff" );
+ applyStyle.accept( "underlineSelectionHeight: 3" );
+ }
+
+ private void menuItem_checkIcon( Consumer applyStyle ) {
+ applyStyle.accept( "icon.checkmarkColor: #fff" );
+ applyStyle.accept( "icon.disabledCheckmarkColor: #fff" );
+ applyStyle.accept( "selectionForeground: #fff" );
+ }
+
+ private void menuItem_arrowIcon( Consumer applyStyle ) {
+ applyStyle.accept( "icon.arrowType: chevron" );
+ applyStyle.accept( "icon.arrowColor: #fff" );
+ applyStyle.accept( "icon.disabledArrowColor: #fff" );
+ applyStyle.accept( "selectionForeground: #fff" );
+ }
+
+ @Test
+ void passwordField() {
+ JPasswordField c = new JPasswordField();
+ FlatPasswordFieldUI ui = (FlatPasswordFieldUI) c.getUI();
+
+ // FlatPasswordFieldUI extends FlatTextFieldUI
+ textField( ui );
+
+ ui.applyStyle( "showCapsLock: true" );
+
+ // capsLockIcon
+ ui.applyStyle( "capsLockIconColor: #fff" );
+
+ // border
+ flatTextBorder( style -> ui.applyStyle( style ) );
+ }
+
+ @Test
+ void popupMenu() {
+ JPopupMenu c = new JPopupMenu();
+ FlatPopupMenuUI ui = (FlatPopupMenuUI) c.getUI();
+
+ ui.applyStyle( "borderInsets: 1,2,3,4" );
+ ui.applyStyle( "borderColor: #fff" );
+
+ // JComponent properties
+ ui.applyStyle( "background: #fff" );
+ ui.applyStyle( "foreground: #fff" );
+ ui.applyStyle( "border: 2,2,2,2,#f00" );
+ }
+
+ @Test
+ void popupMenuSeparator() {
+ JPopupMenu.Separator c = new JPopupMenu.Separator();
+ FlatPopupMenuSeparatorUI ui = (FlatPopupMenuSeparatorUI) c.getUI();
+
+ // FlatPopupMenuSeparatorUI extends FlatSeparatorUI
+ separator( ui, c );
+ }
+
+ @Test
+ void progressBar() {
+ JProgressBar c = new JProgressBar();
+ FlatProgressBarUI ui = (FlatProgressBarUI) c.getUI();
+
+ ui.applyStyle( "arc: 5" );
+ ui.applyStyle( "horizontalSize: 100,12" );
+ ui.applyStyle( "verticalSize: 12,100" );
+
+ // JComponent properties
+ ui.applyStyle( "background: #fff" );
+ ui.applyStyle( "foreground: #fff" );
+ ui.applyStyle( "border: 2,2,2,2,#f00" );
+ }
+
+ @Test
+ void radioButton() {
+ JRadioButton c = new JRadioButton();
+ FlatRadioButtonUI ui = (FlatRadioButtonUI) c.getUI();
+
+ assertTrue( ui.getDefaultIcon() instanceof FlatRadioButtonIcon );
+
+ radioButton( ui, c );
+
+ ui.applyStyle( c, "icon.centerDiameter: 8" );
+ }
+
+ private void radioButton( FlatRadioButtonUI ui, AbstractButton b ) {
+ ui.applyStyle( b, "disabledText: #fff" );
+
+ // JComponent properties
+ ui.applyStyle( b, "background: #fff" );
+ ui.applyStyle( b, "foreground: #fff" );
+ ui.applyStyle( b, "border: 2,2,2,2,#f00" );
+
+ // AbstractButton properties
+ ui.applyStyle( b, "margin: 2,2,2,2" );
+
+ //---- icon ----
+
+ ui.applyStyle( b, "icon.focusWidth: 2" );
+ ui.applyStyle( b, "icon.focusColor: #fff" );
+ ui.applyStyle( b, "icon.arc: 5" );
+
+ // enabled
+ ui.applyStyle( b, "icon.borderColor: #fff" );
+ ui.applyStyle( b, "icon.background: #fff" );
+ ui.applyStyle( b, "icon.selectedBorderColor: #fff" );
+ ui.applyStyle( b, "icon.selectedBackground: #fff" );
+ ui.applyStyle( b, "icon.checkmarkColor: #fff" );
+
+ // disabled
+ ui.applyStyle( b, "icon.disabledBorderColor: #fff" );
+ ui.applyStyle( b, "icon.disabledBackground: #fff" );
+ ui.applyStyle( b, "icon.disabledCheckmarkColor: #fff" );
+
+ // focused
+ ui.applyStyle( b, "icon.focusedBorderColor: #fff" );
+ ui.applyStyle( b, "icon.focusedBackground: #fff" );
+ ui.applyStyle( b, "icon.selectedFocusedBorderColor: #fff" );
+ ui.applyStyle( b, "icon.selectedFocusedBackground: #fff" );
+ ui.applyStyle( b, "icon.selectedFocusedCheckmarkColor: #fff" );
+
+ // hover
+ ui.applyStyle( b, "icon.hoverBorderColor: #fff" );
+ ui.applyStyle( b, "icon.hoverBackground: #fff" );
+ ui.applyStyle( b, "icon.selectedHoverBackground: #fff" );
+
+ // pressed
+ ui.applyStyle( b, "icon.pressedBackground: #fff" );
+ ui.applyStyle( b, "icon.selectedPressedBackground: #fff" );
+ }
+
+ @Test
+ void scrollBar() {
+ JScrollBar c = new JScrollBar();
+ FlatScrollBarUI ui = (FlatScrollBarUI) c.getUI();
+
+ ui.applyStyle( "track: #fff" );
+ ui.applyStyle( "thumb: #fff" );
+ ui.applyStyle( "width: 10" );
+ ui.applyStyle( "minimumThumbSize: 1,2" );
+ ui.applyStyle( "maximumThumbSize: 1,2" );
+ ui.applyStyle( "allowsAbsolutePositioning: true" );
+
+ ui.applyStyle( "trackInsets: 1,2,3,4" );
+ ui.applyStyle( "thumbInsets: 1,2,3,4" );
+ ui.applyStyle( "trackArc: 5" );
+ ui.applyStyle( "thumbArc: 10" );
+ ui.applyStyle( "hoverTrackColor: #fff" );
+ ui.applyStyle( "hoverThumbColor: #fff" );
+ ui.applyStyle( "hoverThumbWithTrack: true" );
+ ui.applyStyle( "pressedTrackColor: #fff" );
+ ui.applyStyle( "pressedThumbColor: #fff" );
+ ui.applyStyle( "pressedThumbWithTrack: true" );
+
+ ui.applyStyle( "showButtons: true" );
+ ui.applyStyle( "arrowType: chevron" );
+ ui.applyStyle( "buttonArrowColor: #fff" );
+ ui.applyStyle( "buttonDisabledArrowColor: #fff" );
+ ui.applyStyle( "hoverButtonBackground: #fff" );
+ ui.applyStyle( "pressedButtonBackground: #fff" );
+
+ // JComponent properties
+ ui.applyStyle( "background: #fff" );
+ ui.applyStyle( "foreground: #fff" );
+ ui.applyStyle( "border: 2,2,2,2,#f00" );
+ }
+
+ @Test
+ void scrollPane() {
+ JScrollPane c = new JScrollPane();
+ FlatScrollPaneUI ui = (FlatScrollPaneUI) c.getUI();
+
+ // border
+ flatBorder( style -> ui.applyStyle( style ) );
+
+ // JComponent properties
+ ui.applyStyle( "background: #fff" );
+ ui.applyStyle( "foreground: #fff" );
+ ui.applyStyle( "border: 2,2,2,2,#f00" );
+ }
+
+ @Test
+ void separator() {
+ JSeparator c = new JSeparator();
+ FlatSeparatorUI ui = (FlatSeparatorUI) c.getUI();
+
+ separator( ui, c );
+ }
+
+ private void separator( FlatSeparatorUI ui, JSeparator c ) {
+ ui.applyStyle( c, "height: 6" );
+ ui.applyStyle( c, "stripeWidth: 2" );
+ ui.applyStyle( c, "stripeIndent: 10" );
+
+ // JComponent properties
+ ui.applyStyle( c, "background: #fff" );
+ ui.applyStyle( c, "foreground: #fff" );
+ ui.applyStyle( c, "border: 2,2,2,2,#f00" );
+ }
+
+ @Test
+ void slider() {
+ JSlider c = new JSlider();
+ FlatSliderUI ui = (FlatSliderUI) c.getUI();
+
+ ui.applyStyle( "trackWidth: 2" );
+ ui.applyStyle( "thumbSize: 12,12" );
+ ui.applyStyle( "focusWidth: 4" );
+
+ ui.applyStyle( "trackValueColor: #fff" );
+ ui.applyStyle( "trackColor: #fff" );
+ ui.applyStyle( "thumbColor: #fff" );
+ ui.applyStyle( "thumbBorderColor: #fff" );
+ ui.applyStyle( "focusedColor: #fff" );
+ ui.applyStyle( "focusedThumbBorderColor: #fff" );
+ ui.applyStyle( "hoverThumbColor: #fff" );
+ ui.applyStyle( "pressedThumbColor: #fff" );
+ ui.applyStyle( "disabledTrackColor: #fff" );
+ ui.applyStyle( "disabledThumbColor: #fff" );
+ ui.applyStyle( "disabledThumbBorderColor: #fff" );
+ ui.applyStyle( "tickColor: #fff" );
+
+ // JComponent properties
+ ui.applyStyle( "background: #fff" );
+ ui.applyStyle( "foreground: #fff" );
+ ui.applyStyle( "border: 2,2,2,2,#f00" );
+
+ // JSlider properties
+ ui.applyStyle( "paintLabels: true" );
+ ui.applyStyle( "paintTicks: true" );
+ ui.applyStyle( "paintTrack: true" );
+ ui.applyStyle( "snapToTicks: true" );
+ }
+
+ @Test
+ void spinner() {
+ JSpinner c = new JSpinner();
+ FlatSpinnerUI ui = (FlatSpinnerUI) c.getUI();
+
+ ui.applyStyle( "minimumWidth: 100" );
+ ui.applyStyle( "buttonStyle: button" );
+ ui.applyStyle( "arrowType: chevron" );
+ ui.applyStyle( "borderColor: #fff" );
+ ui.applyStyle( "disabledBorderColor: #fff" );
+ ui.applyStyle( "disabledBackground: #fff" );
+ ui.applyStyle( "disabledForeground: #fff" );
+ ui.applyStyle( "focusedBackground: #fff" );
+ ui.applyStyle( "buttonBackground: #fff" );
+ ui.applyStyle( "buttonArrowColor: #fff" );
+ ui.applyStyle( "buttonDisabledArrowColor: #fff" );
+ ui.applyStyle( "buttonHoverArrowColor: #fff" );
+ ui.applyStyle( "buttonPressedArrowColor: #fff" );
+ ui.applyStyle( "padding: 1,2,3,4" );
+
+ // border
+ flatRoundBorder( style -> ui.applyStyle( style ) );
+
+ // JComponent properties
+ ui.applyStyle( "background: #fff" );
+ ui.applyStyle( "foreground: #fff" );
+ ui.applyStyle( "border: 2,2,2,2,#f00" );
+ }
+
+ @Test
+ void splitPane() {
+ JSplitPane c = new JSplitPane();
+ FlatSplitPaneUI ui = (FlatSplitPaneUI) c.getUI();
+
+ ui.applyStyle( "arrowType: chevron" );
+ ui.applyStyle( "oneTouchArrowColor: #fff" );
+ ui.applyStyle( "oneTouchHoverArrowColor: #fff" );
+ ui.applyStyle( "oneTouchPressedArrowColor: #fff" );
+
+ ui.applyStyle( "style: grip" );
+ ui.applyStyle( "gripColor: #fff" );
+ ui.applyStyle( "gripDotCount: 3" );
+ ui.applyStyle( "gripDotSize: {integer}3" );
+ ui.applyStyle( "gripGap: 2" );
+
+ // JComponent properties
+ ui.applyStyle( "background: #fff" );
+ ui.applyStyle( "foreground: #fff" );
+ ui.applyStyle( "border: 2,2,2,2,#f00" );
+
+ // JSplitPane properties
+ ui.applyStyle( "dividerSize: {integer}20" );
+ }
+
+ @Test
+ void tabbedPane() {
+ JTabbedPane c = new JTabbedPane();
+ FlatTabbedPaneUI ui = (FlatTabbedPaneUI) c.getUI();
+
+ ui.applyStyle( "tabInsets: 1,2,3,4" );
+ ui.applyStyle( "tabAreaInsets: 1,2,3,4" );
+ ui.applyStyle( "textIconGap: 4" );
+
+ ui.applyStyle( "disabledForeground: #fff" );
+
+ ui.applyStyle( "selectedBackground: #fff" );
+ ui.applyStyle( "selectedForeground: #fff" );
+ ui.applyStyle( "underlineColor: #fff" );
+ ui.applyStyle( "disabledUnderlineColor: #fff" );
+ ui.applyStyle( "hoverColor: #fff" );
+ ui.applyStyle( "focusColor: #fff" );
+ ui.applyStyle( "tabSeparatorColor: #fff" );
+ ui.applyStyle( "contentAreaColor: #fff" );
+
+ ui.applyStyle( "minimumTabWidth: 50" );
+ ui.applyStyle( "maximumTabWidth: 100" );
+ ui.applyStyle( "tabHeight: 30" );
+ ui.applyStyle( "tabSelectionHeight: 3" );
+ ui.applyStyle( "contentSeparatorHeight: 1" );
+ ui.applyStyle( "showTabSeparators: false" );
+ ui.applyStyle( "tabSeparatorsFullHeight: false" );
+ ui.applyStyle( "hasFullBorder: false" );
+ ui.applyStyle( "tabsOpaque: false" );
+
+ ui.applyStyle( "tabsPopupPolicy: asNeeded" );
+ ui.applyStyle( "scrollButtonsPolicy: asNeeded" );
+ ui.applyStyle( "scrollButtonsPlacement: both" );
+
+ ui.applyStyle( "tabAreaAlignment: leading" );
+ ui.applyStyle( "tabAlignment: center" );
+ ui.applyStyle( "tabWidthMode: preferred" );
+
+ ui.applyStyle( "arrowType: chevron" );
+ ui.applyStyle( "buttonInsets: 1,2,3,4" );
+ ui.applyStyle( "buttonArc: 3" );
+ ui.applyStyle( "buttonHoverBackground: #fff" );
+ ui.applyStyle( "buttonPressedBackground: #fff" );
+
+ ui.applyStyle( "moreTabsButtonToolTipText: Gimme more" );
+
+ // FlatTabbedPaneCloseIcon
+ ui.applyStyle( "closeSize: 16,16" );
+ ui.applyStyle( "closeArc: 4" );
+ ui.applyStyle( "closeCrossPlainSize: {float}7.5" );
+ ui.applyStyle( "closeCrossFilledSize: {float}7.5" );
+ ui.applyStyle( "closeCrossLineWidth: {float}1" );
+ ui.applyStyle( "closeBackground: #fff" );
+ ui.applyStyle( "closeForeground: #fff" );
+ ui.applyStyle( "closeHoverBackground: #fff" );
+ ui.applyStyle( "closeHoverForeground: #fff" );
+ ui.applyStyle( "closePressedBackground: #fff" );
+ ui.applyStyle( "closePressedForeground: #fff" );
+
+ // JComponent properties
+ ui.applyStyle( "background: #fff" );
+ ui.applyStyle( "foreground: #fff" );
+ ui.applyStyle( "border: 2,2,2,2,#f00" );
+ }
+
+ @Test
+ void table() {
+ JTable c = new JTable();
+ FlatTableUI ui = (FlatTableUI) c.getUI();
+
+ ui.applyStyle( "showTrailingVerticalLine: true" );
+ ui.applyStyle( "selectionBackground: #fff" );
+ ui.applyStyle( "selectionForeground: #fff" );
+ ui.applyStyle( "selectionInactiveBackground: #fff" );
+ ui.applyStyle( "selectionInactiveForeground: #fff" );
+
+ // FlatTableCellBorder
+ ui.applyStyle( "cellMargins: 1,2,3,4" );
+ ui.applyStyle( "cellFocusColor: #fff" );
+ ui.applyStyle( "showCellFocusIndicator: true" );
+
+ // JComponent properties
+ ui.applyStyle( "background: #fff" );
+ ui.applyStyle( "foreground: #fff" );
+ ui.applyStyle( "border: 2,2,2,2,#f00" );
+
+ // JTable properties
+ ui.applyStyle( "fillsViewportHeight: true" );
+ ui.applyStyle( "rowHeight: 30" );
+ ui.applyStyle( "showHorizontalLines: true" );
+ ui.applyStyle( "showVerticalLines: true" );
+ ui.applyStyle( "intercellSpacing: {dimension}1,1" );
+ }
+
+ @Test
+ void tableHeader() {
+ JTableHeader c = new JTableHeader();
+ FlatTableHeaderUI ui = (FlatTableHeaderUI) c.getUI();
+
+ ui.applyStyle( "bottomSeparatorColor: #fff" );
+ ui.applyStyle( "height: 20" );
+ ui.applyStyle( "sortIconPosition: top" );
+
+ // FlatTableHeaderBorder
+ ui.applyStyle( "cellMargins: 1,2,3,4" );
+ ui.applyStyle( "separatorColor: #fff" );
+ ui.applyStyle( "showTrailingVerticalLine: true" );
+
+ // FlatAscendingSortIcon and FlatDescendingSortIcon
+ ui.applyStyle( "arrowType: chevron" );
+ ui.applyStyle( "sortIconColor: #fff" );
+
+ // JComponent properties
+ ui.applyStyle( "background: #fff" );
+ ui.applyStyle( "foreground: #fff" );
+ ui.applyStyle( "border: 2,2,2,2,#f00" );
+ }
+
+ @Test
+ void textArea() {
+ JTextArea c = new JTextArea();
+ FlatTextAreaUI ui = (FlatTextAreaUI) c.getUI();
+
+ ui.applyStyle( "minimumWidth: 100" );
+ ui.applyStyle( "disabledBackground: #fff" );
+ ui.applyStyle( "inactiveBackground: #fff" );
+ ui.applyStyle( "focusedBackground: #fff" );
+
+ // JComponent properties
+ ui.applyStyle( "background: #fff" );
+ ui.applyStyle( "foreground: #fff" );
+ ui.applyStyle( "border: 2,2,2,2,#f00" );
+
+ // JTextComponent properties
+ ui.applyStyle( "caretColor: #fff" );
+ ui.applyStyle( "selectionColor: #fff" );
+ ui.applyStyle( "selectedTextColor: #fff" );
+ ui.applyStyle( "disabledTextColor: #fff" );
+ }
+
+ @Test
+ void textField() {
+ JTextField c = new JTextField();
+ FlatTextFieldUI ui = (FlatTextFieldUI) c.getUI();
+
+ textField( ui );
+ }
+
+ private void textField( FlatTextFieldUI ui ) {
+ ui.applyStyle( "minimumWidth: 100" );
+ ui.applyStyle( "disabledBackground: #fff" );
+ ui.applyStyle( "inactiveBackground: #fff" );
+ ui.applyStyle( "placeholderForeground: #fff" );
+ ui.applyStyle( "focusedBackground: #fff" );
+
+ // border
+ flatTextBorder( style -> ui.applyStyle( style ) );
+
+ // JComponent properties
+ ui.applyStyle( "background: #fff" );
+ ui.applyStyle( "foreground: #fff" );
+ ui.applyStyle( "border: 2,2,2,2,#f00" );
+
+ // JTextComponent properties
+ ui.applyStyle( "caretColor: #fff" );
+ ui.applyStyle( "selectionColor: #fff" );
+ ui.applyStyle( "selectedTextColor: #fff" );
+ ui.applyStyle( "disabledTextColor: #fff" );
+ }
+
+ @Test
+ void textPane() {
+ JTextPane c = new JTextPane();
+ FlatTextPaneUI ui = (FlatTextPaneUI) c.getUI();
+
+ ui.applyStyle( "minimumWidth: 100" );
+ ui.applyStyle( "disabledBackground: #fff" );
+ ui.applyStyle( "inactiveBackground: #fff" );
+ ui.applyStyle( "focusedBackground: #fff" );
+
+ // JComponent properties
+ ui.applyStyle( "background: #fff" );
+ ui.applyStyle( "foreground: #fff" );
+ ui.applyStyle( "border: 2,2,2,2,#f00" );
+
+ // JTextComponent properties
+ ui.applyStyle( "caretColor: #fff" );
+ ui.applyStyle( "selectionColor: #fff" );
+ ui.applyStyle( "selectedTextColor: #fff" );
+ ui.applyStyle( "disabledTextColor: #fff" );
+ }
+
+ @Test
+ void toggleButton() {
+ JToggleButton b = new JToggleButton();
+ FlatToggleButtonUI ui = (FlatToggleButtonUI) b.getUI();
+
+ // FlatToggleButtonUI extends FlatButtonUI
+ button( b, ui );
+
+ ui.applyStyle( b, "tab.underlineHeight: 3" );
+ ui.applyStyle( b, "tab.underlineColor: #fff" );
+ ui.applyStyle( b, "tab.disabledUnderlineColor: #fff" );
+ ui.applyStyle( b, "tab.selectedBackground: #fff" );
+ ui.applyStyle( b, "tab.hoverBackground: #fff" );
+ ui.applyStyle( b, "tab.focusBackground: #fff" );
+ }
+
+ @Test
+ void toolBar() {
+ JToolBar c = new JToolBar();
+ FlatToolBarUI ui = (FlatToolBarUI) c.getUI();
+
+ ui.applyStyle( "focusableButtons: true" );
+
+ ui.applyStyle( "borderMargins: 1,2,3,4" );
+ ui.applyStyle( "gripColor: #fff" );
+
+ // JComponent properties
+ ui.applyStyle( "background: #fff" );
+ ui.applyStyle( "foreground: #fff" );
+ ui.applyStyle( "border: 2,2,2,2,#f00" );
+
+ // JToolBar properties
+ ui.applyStyle( "borderPainted: true" );
+ ui.applyStyle( "floatable: true" );
+ ui.applyStyle( "margin: 2,2,2,2" );
+ ui.applyStyle( "rollover: true" );
+ }
+
+ @Test
+ void toolBarSeparator() {
+ JToolBar.Separator c = new JToolBar.Separator();
+ FlatToolBarSeparatorUI ui = (FlatToolBarSeparatorUI) c.getUI();
+
+ ui.applyStyle( "separatorWidth: 6" );
+ ui.applyStyle( "separatorColor: #fff" );
+ }
+
+ @Test
+ void tree() {
+ JTree c = new JTree();
+ FlatTreeUI ui = (FlatTreeUI) c.getUI();
+
+ ui.applyStyle( "selectionBackground: #fff" );
+ ui.applyStyle( "selectionForeground: #fff" );
+ ui.applyStyle( "selectionInactiveBackground: #fff" );
+ ui.applyStyle( "selectionInactiveForeground: #fff" );
+ ui.applyStyle( "selectionBorderColor: #fff" );
+ ui.applyStyle( "wideSelection: true" );
+ ui.applyStyle( "showCellFocusIndicator: true" );
+
+ // icons
+ ui.applyStyle( "icon.arrowType: chevron" );
+ ui.applyStyle( "icon.expandedColor: #fff" );
+ ui.applyStyle( "icon.collapsedColor: #fff" );
+ ui.applyStyle( "icon.leafColor: #fff" );
+ ui.applyStyle( "icon.closedColor: #fff" );
+ ui.applyStyle( "icon.openColor: #fff" );
+
+ // JComponent properties
+ ui.applyStyle( "background: #fff" );
+ ui.applyStyle( "foreground: #fff" );
+ ui.applyStyle( "border: 2,2,2,2,#f00" );
+
+ // JTree properties
+ ui.applyStyle( "rootVisible: true" );
+ ui.applyStyle( "rowHeight: 30" );
+ ui.applyStyle( "scrollsOnExpand: true" );
+ ui.applyStyle( "showsRootHandles: true" );
+ ui.applyStyle( "visibleRowCount: 20" );
+ }
+
+ //---- component borders --------------------------------------------------
+
+ private void flatButtonBorder( Consumer applyStyle ) {
+ flatBorder( applyStyle );
+
+ applyStyle.accept( "borderColor: #fff" );
+ applyStyle.accept( "disabledBorderColor: #fff" );
+ applyStyle.accept( "focusedBorderColor: #fff" );
+ applyStyle.accept( "hoverBorderColor: #fff" );
+
+ applyStyle.accept( "default.borderColor: #fff" );
+ applyStyle.accept( "default.focusedBorderColor: #fff" );
+ applyStyle.accept( "default.focusColor: #fff" );
+ applyStyle.accept( "default.hoverBorderColor: #fff" );
+ applyStyle.accept( "toolbar.focusColor: #fff" );
+
+ applyStyle.accept( "borderWidth: 1" );
+ applyStyle.accept( "default.borderWidth: 2" );
+ applyStyle.accept( "toolbar.margin: 1,2,3,4" );
+ applyStyle.accept( "toolbar.spacingInsets: 1,2,3,4" );
+ applyStyle.accept( "toolbar.focusWidth: {float}1.5" );
+ applyStyle.accept( "arc: 6" );
+ }
+
+ private void flatRoundBorder( Consumer applyStyle ) {
+ flatBorder( applyStyle );
+
+ applyStyle.accept( "arc: 6" );
+ }
+
+ private void flatTextBorder( Consumer applyStyle ) {
+ flatBorder( applyStyle );
+
+ applyStyle.accept( "arc: 6" );
+ }
+
+ private void flatBorder( Consumer applyStyle ) {
+ applyStyle.accept( "focusWidth: 2" );
+ applyStyle.accept( "innerFocusWidth: {float}0.5" );
+ applyStyle.accept( "innerOutlineWidth: {float}1.5" );
+ applyStyle.accept( "focusColor: #fff" );
+ applyStyle.accept( "borderColor: #fff" );
+ applyStyle.accept( "disabledBorderColor: #fff" );
+ applyStyle.accept( "focusedBorderColor: #fff" );
+
+ applyStyle.accept( "error.borderColor: #fff" );
+ applyStyle.accept( "error.focusedBorderColor: #fff" );
+ applyStyle.accept( "warning.borderColor: #fff" );
+ applyStyle.accept( "warning.focusedBorderColor: #fff" );
+ applyStyle.accept( "custom.borderColor: desaturate(#f00,50%,relative derived noAutoInverse)" );
+ }
+
+ //---- borders ------------------------------------------------------------
+
+ @Test
+ void flatButtonBorder() {
+ FlatButtonBorder border = new FlatButtonBorder();
+
+ // FlatButtonBorder extends FlatBorder
+ flatBorder( border );
+
+ border.applyStyleProperty( "borderColor", Color.WHITE );
+ border.applyStyleProperty( "disabledBorderColor", Color.WHITE );
+ border.applyStyleProperty( "focusedBorderColor", Color.WHITE );
+ border.applyStyleProperty( "hoverBorderColor", Color.WHITE );
+
+ border.applyStyleProperty( "default.borderColor", Color.WHITE );
+ border.applyStyleProperty( "default.focusedBorderColor", Color.WHITE );
+ border.applyStyleProperty( "default.focusColor", Color.WHITE );
+ border.applyStyleProperty( "default.hoverBorderColor", Color.WHITE );
+ border.applyStyleProperty( "toolbar.focusColor", Color.WHITE );
+
+ border.applyStyleProperty( "borderWidth", 1 );
+ border.applyStyleProperty( "default.borderWidth", 2 );
+ border.applyStyleProperty( "toolbar.margin", new Insets( 1, 2, 3, 4 ) );
+ border.applyStyleProperty( "toolbar.spacingInsets", new Insets( 1, 2, 3, 4 ) );
+ border.applyStyleProperty( "toolbar.focusWidth", 1.5f );
+ border.applyStyleProperty( "arc", 6 );
+ }
+
+ @Test
+ void flatRoundBorder() {
+ FlatRoundBorder border = new FlatRoundBorder();
+
+ // FlatRoundBorder extends FlatBorder
+ flatBorder( border );
+
+ border.applyStyleProperty( "arc", 6 );
+ }
+
+ @Test
+ void flatTextBorder() {
+ FlatTextBorder border = new FlatTextBorder();
+
+ // FlatTextBorder extends FlatBorder
+ flatBorder( border );
+
+ border.applyStyleProperty( "arc", 6 );
+ }
+
+ @Test
+ void flatBorder() {
+ FlatBorder border = new FlatBorder();
+
+ flatBorder( border );
+ }
+
+ private void flatBorder( FlatBorder border ) {
+ border.applyStyleProperty( "focusWidth", 2 );
+ border.applyStyleProperty( "innerFocusWidth", 0.5f );
+ border.applyStyleProperty( "innerOutlineWidth", 1.5f );
+ border.applyStyleProperty( "focusColor", Color.WHITE );
+ border.applyStyleProperty( "borderColor", Color.WHITE );
+ border.applyStyleProperty( "disabledBorderColor", Color.WHITE );
+ border.applyStyleProperty( "focusedBorderColor", Color.WHITE );
+
+ border.applyStyleProperty( "error.borderColor", Color.WHITE );
+ border.applyStyleProperty( "error.focusedBorderColor", Color.WHITE );
+ border.applyStyleProperty( "warning.borderColor", Color.WHITE );
+ border.applyStyleProperty( "warning.focusedBorderColor", Color.WHITE );
+ border.applyStyleProperty( "custom.borderColor", Color.WHITE );
+ }
+
+ //---- icons --------------------------------------------------------------
+
+ @Test
+ void flatCheckBoxIcon() {
+ FlatCheckBoxIcon icon = new FlatCheckBoxIcon();
+
+ flatCheckBoxIcon( icon );
+ }
+
+ @Test
+ void flatRadioButtonIcon() {
+ FlatRadioButtonIcon icon = new FlatRadioButtonIcon();
+
+ // FlatRadioButtonIcon extends FlatCheckBoxIcon
+ flatCheckBoxIcon( icon );
+
+ icon.applyStyleProperty( "centerDiameter", 8 );
+ }
+
+ private void flatCheckBoxIcon( FlatCheckBoxIcon icon ) {
+ icon.applyStyleProperty( "focusWidth", 2 );
+ icon.applyStyleProperty( "focusColor", Color.WHITE );
+ icon.applyStyleProperty( "arc", 5 );
+
+ // enabled
+ icon.applyStyleProperty( "borderColor", Color.WHITE );
+ icon.applyStyleProperty( "background", Color.WHITE );
+ icon.applyStyleProperty( "selectedBorderColor", Color.WHITE );
+ icon.applyStyleProperty( "selectedBackground", Color.WHITE );
+ icon.applyStyleProperty( "checkmarkColor", Color.WHITE );
+
+ // disabled
+ icon.applyStyleProperty( "disabledBorderColor", Color.WHITE );
+ icon.applyStyleProperty( "disabledBackground", Color.WHITE );
+ icon.applyStyleProperty( "disabledCheckmarkColor", Color.WHITE );
+
+ // focused
+ icon.applyStyleProperty( "focusedBorderColor", Color.WHITE );
+ icon.applyStyleProperty( "focusedBackground", Color.WHITE );
+ icon.applyStyleProperty( "selectedFocusedBorderColor", Color.WHITE );
+ icon.applyStyleProperty( "selectedFocusedBackground", Color.WHITE );
+ icon.applyStyleProperty( "selectedFocusedCheckmarkColor", Color.WHITE );
+
+ // hover
+ icon.applyStyleProperty( "hoverBorderColor", Color.WHITE );
+ icon.applyStyleProperty( "hoverBackground", Color.WHITE );
+ icon.applyStyleProperty( "selectedHoverBackground", Color.WHITE );
+
+ // pressed
+ icon.applyStyleProperty( "pressedBackground", Color.WHITE );
+ icon.applyStyleProperty( "selectedPressedBackground", Color.WHITE );
+ }
+
+ @Test
+ void flatCheckBoxMenuItemIcon() {
+ FlatCheckBoxMenuItemIcon icon = new FlatCheckBoxMenuItemIcon();
+
+ flatCheckBoxMenuItemIcon( icon );
+ }
+
+ @Test
+ void flatRadioButtonMenuItemIcon() {
+ FlatRadioButtonMenuItemIcon icon = new FlatRadioButtonMenuItemIcon();
+
+ // FlatRadioButtonMenuItemIcon extends FlatCheckBoxMenuItemIcon
+ flatCheckBoxMenuItemIcon( icon );
+ }
+
+ private void flatCheckBoxMenuItemIcon( FlatCheckBoxMenuItemIcon icon ) {
+ icon.applyStyleProperty( "checkmarkColor", Color.WHITE );
+ icon.applyStyleProperty( "disabledCheckmarkColor", Color.WHITE );
+ icon.applyStyleProperty( "selectionForeground", Color.WHITE );
+ }
+
+ @Test
+ void flatMenuArrowIcon() {
+ FlatMenuArrowIcon icon = new FlatMenuArrowIcon();
+
+ flatMenuArrowIcon( icon );
+ }
+
+ @Test
+ void flatMenuItemArrowIcon() {
+ FlatMenuItemArrowIcon icon = new FlatMenuItemArrowIcon();
+
+ // FlatMenuItemArrowIcon extends FlatMenuArrowIcon
+ flatMenuArrowIcon( icon );
+ }
+
+ private void flatMenuArrowIcon( FlatMenuArrowIcon icon ) {
+ icon.applyStyleProperty( "arrowType", "chevron" );
+ icon.applyStyleProperty( "arrowColor", Color.WHITE );
+ icon.applyStyleProperty( "disabledArrowColor", Color.WHITE );
+ icon.applyStyleProperty( "selectionForeground", Color.WHITE );
+ }
+
+ @Test
+ void flatHelpButtonIcon() {
+ FlatHelpButtonIcon icon = new FlatHelpButtonIcon();
+
+ icon.applyStyleProperty( "focusWidth", 2 );
+ icon.applyStyleProperty( "focusColor", Color.WHITE );
+ icon.applyStyleProperty( "innerFocusWidth", 0.5f );
+ icon.applyStyleProperty( "borderWidth", 1 );
+
+ icon.applyStyleProperty( "borderColor", Color.WHITE );
+ icon.applyStyleProperty( "disabledBorderColor", Color.WHITE );
+ icon.applyStyleProperty( "focusedBorderColor", Color.WHITE );
+ icon.applyStyleProperty( "hoverBorderColor", Color.WHITE );
+ icon.applyStyleProperty( "background", Color.WHITE );
+ icon.applyStyleProperty( "disabledBackground", Color.WHITE );
+ icon.applyStyleProperty( "focusedBackground", Color.WHITE );
+ icon.applyStyleProperty( "hoverBackground", Color.WHITE );
+ icon.applyStyleProperty( "pressedBackground", Color.WHITE );
+ icon.applyStyleProperty( "questionMarkColor", Color.WHITE );
+ icon.applyStyleProperty( "disabledQuestionMarkColor", Color.WHITE );
+ }
+
+ @Test
+ void flatClearIcon() {
+ FlatClearIcon icon = new FlatClearIcon();
+
+ icon.applyStyleProperty( "clearIconColor", Color.WHITE );
+ icon.applyStyleProperty( "clearIconHoverColor", Color.WHITE );
+ icon.applyStyleProperty( "clearIconPressedColor", Color.WHITE );
+ }
+
+ @Test
+ void flatSearchIcon() {
+ FlatSearchIcon icon = new FlatSearchIcon();
+
+ flatSearchIcon( icon );
+ }
+
+ @Test
+ void flatSearchWithHistoryIcon() {
+ FlatSearchWithHistoryIcon icon = new FlatSearchWithHistoryIcon();
+
+ flatSearchIcon( icon );
+ }
+
+ private void flatSearchIcon( FlatSearchIcon icon ) {
+ icon.applyStyleProperty( "searchIconColor", Color.WHITE );
+ icon.applyStyleProperty( "searchIconHoverColor", Color.WHITE );
+ icon.applyStyleProperty( "searchIconPressedColor", Color.WHITE );
+ }
+}
diff --git a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatTextComponents.java b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatTextComponents.java
new file mode 100644
index 00000000..7b70d134
--- /dev/null
+++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatTextComponents.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2021 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.ui;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static com.formdev.flatlaf.FlatClientProperties.STYLE;
+import java.util.function.Supplier;
+import javax.swing.JEditorPane;
+import javax.swing.JFormattedTextField;
+import javax.swing.JPasswordField;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.JTextPane;
+import javax.swing.UIManager;
+import javax.swing.plaf.ColorUIResource;
+import javax.swing.plaf.basic.BasicTextFieldUI;
+import javax.swing.text.JTextComponent;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author Karl Tauber
+ */
+public class TestFlatTextComponents
+{
+ @BeforeAll
+ static void setup() {
+ TestUtils.setup( false );
+ }
+
+ @AfterAll
+ static void cleanup() {
+ TestUtils.cleanup();
+ }
+
+ @Test
+ void editorPane_updateBackground() {
+ textComponent_updateBackground( "EditorPane", JEditorPane::new );
+ }
+
+ @Test
+ void formattedTextField_updateBackground() {
+ textComponent_updateBackground( "FormattedTextField", JFormattedTextField::new );
+ }
+
+ @Test
+ void passwordField_updateBackground() {
+ textComponent_updateBackground( "PasswordField", JPasswordField::new );
+ }
+
+ @Test
+ void textArea_updateBackground() {
+ textComponent_updateBackground( "TextArea", JTextArea::new );
+ }
+
+ @Test
+ void textField_updateBackground() {
+ textComponent_updateBackground( "TextField", JTextField::new );
+ }
+
+ @Test
+ void textPane_updateBackground() {
+ textComponent_updateBackground( "TextPane", JTextPane::new );
+ }
+
+ @Test
+ void basicTextField_updateBackground() {
+ textComponent_updateBackground( "TextField", () -> {
+ JTextField c = new JTextField();
+ c.setUI( new BasicTextFieldUI() );
+ return c;
+ } );
+ }
+
+ private void textComponent_updateBackground( String prefix, Supplier createTextComponent ) {
+ ColorUIResource background = new ColorUIResource( 0xff0000 );
+ ColorUIResource inactiveBackground = new ColorUIResource( 0x00ff00 );
+ ColorUIResource disabledBackground = new ColorUIResource( 0x0000ff );
+
+ UIManager.put( prefix + ".background", background );
+ UIManager.put( prefix + ".inactiveBackground", inactiveBackground );
+ UIManager.put( prefix + ".disabledBackground", disabledBackground );
+
+ JTextComponent c = createTextComponent.get();
+
+ // without styling
+ assertEquals( background, c.getBackground() );
+ c.setEditable( false ); assertEquals( inactiveBackground, c.getBackground() );
+ c.setEnabled( false ); assertEquals( disabledBackground, c.getBackground() );
+ c.setEditable( true ); assertEquals( disabledBackground, c.getBackground() );
+ c.setEnabled( true ); assertEquals( background, c.getBackground() );
+
+
+ if( !c.getUI().getClass().getSimpleName().startsWith( "Flat" ) )
+ return;
+
+
+ // with styling
+
+ ColorUIResource inactiveBackground1 = new ColorUIResource( 0x00ee00 );
+ ColorUIResource disabledBackground1 = new ColorUIResource( 0x0000ee );
+ ColorUIResource inactiveBackground2 = new ColorUIResource( 0x00dd00 );
+ ColorUIResource disabledBackground2 = new ColorUIResource( 0x0000dd );
+ String style1 = "inactiveBackground: #00ee00; disabledBackground: #0000ee";
+ String style2 = "inactiveBackground: #00dd00; disabledBackground: #0000dd";
+
+ c.putClientProperty( STYLE, style1 );
+
+ assertEquals( background, c.getBackground() );
+ c.setEditable( false ); assertEquals( inactiveBackground1, c.getBackground() );
+ c.setEnabled( false ); assertEquals( disabledBackground1, c.getBackground() );
+ c.setEditable( true ); assertEquals( disabledBackground1, c.getBackground() );
+ c.setEnabled( true ); assertEquals( background, c.getBackground() );
+
+ c.putClientProperty( STYLE, null );
+ assertEquals( background, c.getBackground() );
+
+ c.setEditable( false );
+ c.putClientProperty( STYLE, style1 );
+ assertEquals( inactiveBackground1, c.getBackground() );
+
+ c.putClientProperty( STYLE, null );
+ assertEquals( inactiveBackground, c.getBackground() );
+
+ c.setEnabled( false );
+ c.putClientProperty( STYLE, style1 );
+ assertEquals( disabledBackground1, c.getBackground() );
+
+
+ // change from style1 to style2
+ c.putClientProperty( STYLE, style2 );
+ assertEquals( disabledBackground2, c.getBackground() );
+
+ c.setEnabled( true );
+ assertEquals( inactiveBackground2, c.getBackground() );
+
+
+ // remove style
+ c.putClientProperty( STYLE, null );
+ assertEquals( inactiveBackground, c.getBackground() );
+ }
+}
diff --git a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestUtils.java b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestUtils.java
index e4bead06..d9399383 100644
--- a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestUtils.java
+++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestUtils.java
@@ -17,7 +17,10 @@
package com.formdev.flatlaf.ui;
import java.awt.Font;
+import java.util.Map;
+import java.util.Objects;
import javax.swing.UIManager;
+import org.opentest4j.AssertionFailedError;
import com.formdev.flatlaf.FlatIntelliJLaf;
import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.FlatSystemProperties;
@@ -50,4 +53,15 @@ public class TestUtils
public static void resetFont() {
UIManager.put( "defaultFont", null );
}
+
+ public static void assertMapEquals( Map, ?> expected, Map, ?> actual ) {
+ if( !Objects.equals( expected, actual ) ) {
+ String expectedStr = String.valueOf( expected ).replace( ", ", ",\n" );
+ String actualStr = String.valueOf( actual ).replace( ", ", ",\n" );
+ String msg = String.format( "expected: <%s> but was: <%s>", expectedStr, actualStr );
+
+ // pass expected/actual strings to exception for nice diff in IDE
+ throw new AssertionFailedError( msg, expectedStr, actualStr );
+ }
+ }
}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatButton.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatButton.java
index 38434c28..4b290775 100644
--- a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatButton.java
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatButton.java
@@ -27,7 +27,7 @@ import javax.swing.JButton;
*/
public class FlatButton
extends JButton
- implements FlatComponentExtension
+ implements FlatComponentExtension, FlatStyleableComponent
{
// NOTE: enum names must be equal to allowed strings
public enum ButtonType { none, square, roundRect, tab, help, toolBarButton, borderless }
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatCheckBox.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatCheckBox.java
new file mode 100644
index 00000000..bfdc60b5
--- /dev/null
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatCheckBox.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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.extras.components;
+
+import javax.swing.JCheckBox;
+
+/**
+ * Subclass of {@link JCheckBox} that provides easy access to FlatLaf specific client properties.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public class FlatCheckBox
+ extends JCheckBox
+ implements FlatStyleableComponent
+{
+}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatCheckBoxMenuItem.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatCheckBoxMenuItem.java
new file mode 100644
index 00000000..711d91e0
--- /dev/null
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatCheckBoxMenuItem.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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.extras.components;
+
+import javax.swing.JCheckBoxMenuItem;
+
+/**
+ * Subclass of {@link JCheckBoxMenuItem} that provides easy access to FlatLaf specific client properties.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public class FlatCheckBoxMenuItem
+ extends JCheckBoxMenuItem
+ implements FlatStyleableComponent
+{
+}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatComboBox.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatComboBox.java
index 6b9581c7..79e44158 100644
--- a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatComboBox.java
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatComboBox.java
@@ -27,7 +27,7 @@ import javax.swing.JComboBox;
*/
public class FlatComboBox
extends JComboBox
- implements FlatComponentExtension
+ implements FlatComponentExtension, FlatStyleableComponent
{
/**
* Returns the placeholder text that is only painted if the editable combo box is empty.
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatEditorPane.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatEditorPane.java
index 403ccb43..02be8eff 100644
--- a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatEditorPane.java
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatEditorPane.java
@@ -26,7 +26,7 @@ import javax.swing.JEditorPane;
*/
public class FlatEditorPane
extends JEditorPane
- implements FlatComponentExtension
+ implements FlatComponentExtension, FlatStyleableComponent
{
/**
* Returns minimum width of a component.
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatFormattedTextField.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatFormattedTextField.java
index 4ebc554d..fe030a95 100644
--- a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatFormattedTextField.java
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatFormattedTextField.java
@@ -29,7 +29,7 @@ import com.formdev.flatlaf.extras.components.FlatTextField.SelectAllOnFocusPolic
*/
public class FlatFormattedTextField
extends JFormattedTextField
- implements FlatComponentExtension
+ implements FlatComponentExtension, FlatStyleableComponent
{
/**
* Returns the placeholder text that is only painted if the text field is empty.
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatInternalFrame.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatInternalFrame.java
new file mode 100644
index 00000000..cdc09538
--- /dev/null
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatInternalFrame.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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.extras.components;
+
+import javax.swing.JInternalFrame;
+
+/**
+ * Subclass of {@link JInternalFrame} that provides easy access to FlatLaf specific client properties.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public class FlatInternalFrame
+ extends JInternalFrame
+ implements FlatStyleableComponent
+{
+}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatLabel.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatLabel.java
new file mode 100644
index 00000000..2577b9a4
--- /dev/null
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatLabel.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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.extras.components;
+
+import javax.swing.JLabel;
+
+/**
+ * Subclass of {@link JLabel} that provides easy access to FlatLaf specific client properties.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public class FlatLabel
+ extends JLabel
+ implements FlatStyleableComponent
+{
+}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatList.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatList.java
new file mode 100644
index 00000000..b667e907
--- /dev/null
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatList.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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.extras.components;
+
+import javax.swing.JList;
+
+/**
+ * Subclass of {@link JList} that provides easy access to FlatLaf specific client properties.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public class FlatList
+ extends JList
+ implements FlatStyleableComponent
+{
+}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatMenu.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatMenu.java
new file mode 100644
index 00000000..407ad500
--- /dev/null
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatMenu.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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.extras.components;
+
+import javax.swing.JMenu;
+
+/**
+ * Subclass of {@link JMenu} that provides easy access to FlatLaf specific client properties.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public class FlatMenu
+ extends JMenu
+ implements FlatStyleableComponent
+{
+}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatMenuBar.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatMenuBar.java
new file mode 100644
index 00000000..b7e2520b
--- /dev/null
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatMenuBar.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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.extras.components;
+
+import javax.swing.JMenuBar;
+
+/**
+ * Subclass of {@link JMenuBar} that provides easy access to FlatLaf specific client properties.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public class FlatMenuBar
+ extends JMenuBar
+ implements FlatStyleableComponent
+{
+}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatMenuItem.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatMenuItem.java
new file mode 100644
index 00000000..5895a9aa
--- /dev/null
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatMenuItem.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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.extras.components;
+
+import javax.swing.JMenuItem;
+
+/**
+ * Subclass of {@link JMenuItem} that provides easy access to FlatLaf specific client properties.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public class FlatMenuItem
+ extends JMenuItem
+ implements FlatStyleableComponent
+{
+}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatPasswordField.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatPasswordField.java
index 86495da2..3a9b027a 100644
--- a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatPasswordField.java
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatPasswordField.java
@@ -29,7 +29,7 @@ import com.formdev.flatlaf.extras.components.FlatTextField.SelectAllOnFocusPolic
*/
public class FlatPasswordField
extends JPasswordField
- implements FlatComponentExtension
+ implements FlatComponentExtension, FlatStyleableComponent
{
/**
* Returns the placeholder text that is only painted if the text field is empty.
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatPopupMenu.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatPopupMenu.java
new file mode 100644
index 00000000..e16757e5
--- /dev/null
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatPopupMenu.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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.extras.components;
+
+import javax.swing.JPopupMenu;
+
+/**
+ * Subclass of {@link JPopupMenu} that provides easy access to FlatLaf specific client properties.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public class FlatPopupMenu
+ extends JPopupMenu
+ implements FlatStyleableComponent
+{
+}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatPopupMenuSeparator.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatPopupMenuSeparator.java
new file mode 100644
index 00000000..37f0abb2
--- /dev/null
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatPopupMenuSeparator.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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.extras.components;
+
+import javax.swing.JPopupMenu;
+
+/**
+ * Subclass of {@link JPopupMenu.Separator} that provides easy access to FlatLaf specific client properties.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public class FlatPopupMenuSeparator
+ extends JPopupMenu.Separator
+ implements FlatStyleableComponent
+{
+}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatProgressBar.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatProgressBar.java
index c4698374..d0ede426 100644
--- a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatProgressBar.java
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatProgressBar.java
@@ -26,7 +26,7 @@ import javax.swing.JProgressBar;
*/
public class FlatProgressBar
extends JProgressBar
- implements FlatComponentExtension
+ implements FlatComponentExtension, FlatStyleableComponent
{
/**
* Returns whether the progress bar has always the larger height even if no string is painted.
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatRadioButton.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatRadioButton.java
new file mode 100644
index 00000000..f19b0a04
--- /dev/null
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatRadioButton.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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.extras.components;
+
+import javax.swing.JRadioButton;
+
+/**
+ * Subclass of {@link JRadioButton} that provides easy access to FlatLaf specific client properties.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public class FlatRadioButton
+ extends JRadioButton
+ implements FlatStyleableComponent
+{
+}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatRadioButtonMenuItem.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatRadioButtonMenuItem.java
new file mode 100644
index 00000000..6185ba61
--- /dev/null
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatRadioButtonMenuItem.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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.extras.components;
+
+import javax.swing.JRadioButtonMenuItem;
+
+/**
+ * Subclass of {@link JRadioButtonMenuItem} that provides easy access to FlatLaf specific client properties.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public class FlatRadioButtonMenuItem
+ extends JRadioButtonMenuItem
+ implements FlatStyleableComponent
+{
+}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatScrollBar.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatScrollBar.java
index eba79afe..7d0a2fd1 100644
--- a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatScrollBar.java
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatScrollBar.java
@@ -26,7 +26,7 @@ import javax.swing.JScrollBar;
*/
public class FlatScrollBar
extends JScrollBar
- implements FlatComponentExtension
+ implements FlatComponentExtension, FlatStyleableComponent
{
/**
* Returns whether the decrease/increase arrow buttons of a scrollbar are shown.
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatScrollPane.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatScrollPane.java
index 159b477d..f99f0d3b 100644
--- a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatScrollPane.java
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatScrollPane.java
@@ -27,7 +27,7 @@ import javax.swing.JScrollPane;
*/
public class FlatScrollPane
extends JScrollPane
- implements FlatComponentExtension
+ implements FlatComponentExtension, FlatStyleableComponent
{
/**
* Returns whether the decrease/increase arrow buttons of a scrollbar are shown.
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatSeparator.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatSeparator.java
new file mode 100644
index 00000000..aa8b9a56
--- /dev/null
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatSeparator.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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.extras.components;
+
+import javax.swing.JSeparator;
+
+/**
+ * Subclass of {@link JSeparator} that provides easy access to FlatLaf specific client properties.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public class FlatSeparator
+ extends JSeparator
+ implements FlatStyleableComponent
+{
+}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatSlider.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatSlider.java
new file mode 100644
index 00000000..aa9aa6e5
--- /dev/null
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatSlider.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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.extras.components;
+
+import javax.swing.JSlider;
+
+/**
+ * Subclass of {@link JSlider} that provides easy access to FlatLaf specific client properties.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public class FlatSlider
+ extends JSlider
+ implements FlatStyleableComponent
+{
+}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatSpinner.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatSpinner.java
index f4e162f0..d9567bad 100644
--- a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatSpinner.java
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatSpinner.java
@@ -27,7 +27,7 @@ import javax.swing.JSpinner;
*/
public class FlatSpinner
extends JSpinner
- implements FlatComponentExtension
+ implements FlatComponentExtension, FlatStyleableComponent
{
/**
* Returns minimum width of a component.
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatSplitPane.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatSplitPane.java
new file mode 100644
index 00000000..846ad68e
--- /dev/null
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatSplitPane.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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.extras.components;
+
+import javax.swing.JSplitPane;
+
+/**
+ * Subclass of {@link JSplitPane} that provides easy access to FlatLaf specific client properties.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public class FlatSplitPane
+ extends JSplitPane
+ implements FlatStyleableComponent
+{
+}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatStyleableComponent.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatStyleableComponent.java
new file mode 100644
index 00000000..d2b3c70c
--- /dev/null
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatStyleableComponent.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2021 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.extras.components;
+
+import java.util.Map;
+import javax.swing.JComponent;
+import com.formdev.flatlaf.FlatClientProperties;
+
+/**
+ * Interface for all styleable FlatLaf components.
+ *
+ * If you already have custom subclasses of Swing components, you can add this interface
+ * to your components to add styling getter and setter methods to them.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public interface FlatStyleableComponent
+{
+ /**
+ * Returns the style of a component as String in CSS syntax ("key1: value1; key2: value2; ...")
+ * or {@code null} if no style has been assigned.
+ */
+ default String getStyle() {
+ return (String) getClientProperty( FlatClientProperties.STYLE );
+ }
+
+ /**
+ * Specifies the style of a component as String in CSS syntax ("key1: value1; key2: value2; ...").
+ *
+ * The keys are the same as used in UI defaults, but without component type prefix.
+ * E.g. for UI default {@code Slider.thumbSize} use key {@code thumbSize}.
+ *
+ * The syntax of the CSS values is the same as used in FlatLaf properties files
+ * (https://www.formdev.com/flatlaf/properties-files/ ),
+ * but some features are not supported (e.g. variables).
+ */
+ default void setStyle( String style ) {
+ putClientProperty( FlatClientProperties.STYLE, style );
+ }
+
+
+ /**
+ * Returns the style of a component as {@link java.util.Map}<String, Object>
+ * or {@code null} if no style has been assigned.
+ */
+ @SuppressWarnings( "unchecked" )
+ default Map getStyleMap() {
+ return (Map) getClientProperty( FlatClientProperties.STYLE );
+ }
+
+ /**
+ * Specifies the style of a component as {@link java.util.Map}<String, Object> with binary values.
+ *
+ * The keys are the same as used in UI defaults, but without component type prefix.
+ * E.g. for UI default {@code Slider.thumbSize} use key {@code thumbSize}.
+ *
+ * The values are not parsed from a string. They must be binary.
+ */
+ default void setStyleMap( Map styleMap ) {
+ putClientProperty( FlatClientProperties.STYLE, styleMap );
+ }
+
+
+ /**
+ * Overrides {@link JComponent#getClientProperty(Object)}.
+ */
+ Object getClientProperty( Object key );
+
+ /**
+ * Overrides {@link JComponent#putClientProperty(Object, Object)}.
+ */
+ void putClientProperty( Object key, Object value );
+}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTabbedPane.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTabbedPane.java
index bd04f9b4..22181f90 100644
--- a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTabbedPane.java
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTabbedPane.java
@@ -31,7 +31,7 @@ import javax.swing.SwingConstants;
*/
public class FlatTabbedPane
extends JTabbedPane
- implements FlatComponentExtension
+ implements FlatComponentExtension, FlatStyleableComponent
{
/**
* Returns whether separators are shown between tabs.
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTable.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTable.java
new file mode 100644
index 00000000..627517d0
--- /dev/null
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTable.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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.extras.components;
+
+import javax.swing.JTable;
+
+/**
+ * Subclass of {@link JTable} that provides easy access to FlatLaf specific client properties.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public class FlatTable
+ extends JTable
+ implements FlatStyleableComponent
+{
+}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTableHeader.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTableHeader.java
new file mode 100644
index 00000000..37e0e209
--- /dev/null
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTableHeader.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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.extras.components;
+
+import javax.swing.table.JTableHeader;
+
+/**
+ * Subclass of {@link JTableHeader} that provides easy access to FlatLaf specific client properties.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public class FlatTableHeader
+ extends JTableHeader
+ implements FlatStyleableComponent
+{
+}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTextArea.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTextArea.java
index a0568d97..5ba1f7ac 100644
--- a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTextArea.java
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTextArea.java
@@ -26,7 +26,7 @@ import javax.swing.JTextArea;
*/
public class FlatTextArea
extends JTextArea
- implements FlatComponentExtension
+ implements FlatComponentExtension, FlatStyleableComponent
{
/**
* Returns minimum width of a component.
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTextField.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTextField.java
index bfa22dd2..8c8cc56e 100644
--- a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTextField.java
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTextField.java
@@ -28,7 +28,7 @@ import javax.swing.JTextField;
*/
public class FlatTextField
extends JTextField
- implements FlatComponentExtension
+ implements FlatComponentExtension, FlatStyleableComponent
{
/**
* Returns the placeholder text that is only painted if the text field is empty.
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTextPane.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTextPane.java
index 4660068a..84897ee6 100644
--- a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTextPane.java
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTextPane.java
@@ -26,7 +26,7 @@ import javax.swing.JTextPane;
*/
public class FlatTextPane
extends JTextPane
- implements FlatComponentExtension
+ implements FlatComponentExtension, FlatStyleableComponent
{
/**
* Returns minimum width of a component.
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatToggleButton.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatToggleButton.java
index a342f890..226acb12 100644
--- a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatToggleButton.java
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatToggleButton.java
@@ -28,7 +28,7 @@ import com.formdev.flatlaf.extras.components.FlatButton.ButtonType;
*/
public class FlatToggleButton
extends JToggleButton
- implements FlatComponentExtension
+ implements FlatComponentExtension, FlatStyleableComponent
{
/**
* Returns type of a button.
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatToolBar.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatToolBar.java
new file mode 100644
index 00000000..3caff752
--- /dev/null
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatToolBar.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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.extras.components;
+
+import javax.swing.JToolBar;
+
+/**
+ * Subclass of {@link JToolBar} that provides easy access to FlatLaf specific client properties.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public class FlatToolBar
+ extends JToolBar
+ implements FlatStyleableComponent
+{
+}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatToolBarSeparator.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatToolBarSeparator.java
new file mode 100644
index 00000000..37300b5d
--- /dev/null
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatToolBarSeparator.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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.extras.components;
+
+import javax.swing.JToolBar;
+
+/**
+ * Subclass of {@link JToolBar.Separator} that provides easy access to FlatLaf specific client properties.
+ *
+ * @author Karl Tauber
+ * @since 2
+ */
+public class FlatToolBarSeparator
+ extends JToolBar.Separator
+ implements FlatStyleableComponent
+{
+}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTree.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTree.java
index 730b91be..83c0075c 100644
--- a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTree.java
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTree.java
@@ -22,10 +22,11 @@ import javax.swing.JTree;
/**
* Subclass of {@link JTree} that provides easy access to FlatLaf specific client properties.
*
+ * @author Karl Tauber
*/
public class FlatTree
extends JTree
- implements FlatComponentExtension
+ implements FlatComponentExtension, FlatStyleableComponent
{
/**
* Returns if the tree shows a wide selection
diff --git a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/UIDefaultsLoaderAccessor.java b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/UIDefaultsLoaderAccessor.java
index fb4698d1..d6b35f90 100644
--- a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/UIDefaultsLoaderAccessor.java
+++ b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/UIDefaultsLoaderAccessor.java
@@ -56,7 +56,8 @@ public class UIDefaultsLoaderAccessor
Function resolver )
{
ValueType[] resultValueType2 = new ValueType[1];
- Object result = UIDefaultsLoader.parseValue( key, value, resultValueType2, resolver, Collections.emptyList() );
+ Object result = UIDefaultsLoader.parseValue( key, value, null,
+ resultValueType2, resolver, Collections.emptyList() );
resultValueType[0] = resultValueType2[0];
return result;
}
diff --git a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeFileEditor.java b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeFileEditor.java
index 0ba5110f..e3ad1e27 100644
--- a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeFileEditor.java
+++ b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeFileEditor.java
@@ -130,6 +130,11 @@ class FlatThemeFileEditor
if( UIManager.getLookAndFeel() instanceof FlatDarkLaf )
darkLafMenuItem.setSelected( true );
+ // highlight selected tab
+ tabbedPane.setStyle(
+ "[light]selectedBackground: lighten($TabbedPane.background,5%);" +
+ " [dark]selectedBackground: darken($TabbedPane.background,5%)" );
+
// add "+" button to tabbed pane
newButton = new JButton( new FlatSVGIcon( "com/formdev/flatlaf/themeeditor/icons/add.svg" ) );
newButton.setToolTipText( "New Properties File" );
diff --git a/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatDarkLaf.properties b/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatDarkLaf.properties
index 04e68e5d..d274c2e0 100644
--- a/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatDarkLaf.properties
+++ b/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatDarkLaf.properties
@@ -14,11 +14,6 @@
# limitations under the License.
#
-#---- TabbedPane ----
-
-#TabbedPane.selectedBackground = darken($TabbedPane.background,5%)
-
-
#---- FlatThemeEditorPane ----
FlatThemeEditorPane.background = #2b2b2b
diff --git a/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLightLaf.properties b/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLightLaf.properties
index d84c0966..0678c339 100644
--- a/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLightLaf.properties
+++ b/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLightLaf.properties
@@ -14,11 +14,6 @@
# limitations under the License.
#
-#---- TabbedPane ----
-
-#TabbedPane.selectedBackground = lighten($TabbedPane.background,5%)
-
-
#---- FlatThemeEditorPane ----
FlatThemeEditorPane.background = #fff