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 e44147cb..21549b5a 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 @@ -16,7 +16,6 @@ package com.formdev.flatlaf.ui; -import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.beans.PropertyChangeListener; @@ -26,6 +25,8 @@ import javax.swing.JComponent; import javax.swing.LookAndFeel; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI; +import javax.swing.plaf.basic.BasicMenuItemUI; +import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException; import com.formdev.flatlaf.util.LoggingFacade; @@ -58,6 +59,12 @@ import com.formdev.flatlaf.util.LoggingFacade; * * @author Karl Tauber */ +@StyleableField( cls=BasicMenuItemUI.class, key="selectionBackground" ) +@StyleableField( cls=BasicMenuItemUI.class, key="selectionForeground" ) +@StyleableField( cls=BasicMenuItemUI.class, key="disabledForeground" ) +@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorForeground" ) +@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorSelectionForeground" ) + public class FlatCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI implements StyleableUI @@ -125,23 +132,13 @@ public class FlatCheckBoxMenuItemUI // ignore } - Object oldValue; - switch( key ) { - // BasicMenuItemUI - case "selectionBackground": oldValue = selectionBackground; selectionBackground = (Color) value; return oldValue; - case "selectionForeground": oldValue = selectionForeground; selectionForeground = (Color) value; return oldValue; - case "disabledForeground": oldValue = disabledForeground; disabledForeground = (Color) value; return oldValue; - case "acceleratorForeground": oldValue = acceleratorForeground; acceleratorForeground = (Color) value; return oldValue; - case "acceleratorSelectionForeground": oldValue = acceleratorSelectionForeground; acceleratorSelectionForeground = (Color) value; return oldValue; - } - return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, menuItem, key, value ); } /** @since 2 */ @Override public Map> getStyleableInfos( JComponent c ) { - return FlatMenuItemUI.getStyleableInfos( renderer ); + return FlatMenuItemUI.getStyleableInfos( this, renderer ); } @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 0089bd98..b73cbdd4 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 @@ -70,6 +70,7 @@ import javax.swing.plaf.basic.BasicComboPopup; import javax.swing.plaf.basic.ComboPopup; import javax.swing.text.JTextComponent; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; +import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.SystemInfo; @@ -117,6 +118,8 @@ import com.formdev.flatlaf.util.SystemInfo; * * @author Karl Tauber */ +@StyleableField( cls=BasicComboBoxUI.class, key="padding" ) + public class FlatComboBoxUI extends BasicComboBoxUI implements StyleableUI @@ -491,13 +494,6 @@ public class FlatComboBoxUI /** @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 ); @@ -506,11 +502,7 @@ public class FlatComboBoxUI /** @since 2 */ @Override public Map> getStyleableInfos( JComponent c ) { - Map> infos = new FlatStylingSupport.StyleableInfosMap<>(); - infos.put( "padding", Insets.class ); - FlatStylingSupport.collectAnnotatedStyleableInfos( this, infos ); - FlatStylingSupport.collectStyleableInfos( comboBox.getBorder(), infos ); - return infos; + return FlatStylingSupport.getAnnotatedStyleableInfos( this, comboBox.getBorder() ); } @Override 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 fdd15bac..aaaec9d8 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,7 +16,6 @@ package com.formdev.flatlaf.ui; -import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.beans.PropertyChangeListener; @@ -26,6 +25,7 @@ import javax.swing.JComponent; import javax.swing.LookAndFeel; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicMenuItemUI; +import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException; import com.formdev.flatlaf.util.LoggingFacade; @@ -58,6 +58,12 @@ import com.formdev.flatlaf.util.LoggingFacade; * * @author Karl Tauber */ +@StyleableField( cls=BasicMenuItemUI.class, key="selectionBackground" ) +@StyleableField( cls=BasicMenuItemUI.class, key="selectionForeground" ) +@StyleableField( cls=BasicMenuItemUI.class, key="disabledForeground" ) +@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorForeground" ) +@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorSelectionForeground" ) + public class FlatMenuItemUI extends BasicMenuItemUI implements StyleableUI @@ -125,32 +131,17 @@ public class FlatMenuItemUI // ignore } - Object oldValue; - switch( key ) { - // BasicMenuItemUI - case "selectionBackground": oldValue = selectionBackground; selectionBackground = (Color) value; return oldValue; - case "selectionForeground": oldValue = selectionForeground; selectionForeground = (Color) value; return oldValue; - case "disabledForeground": oldValue = disabledForeground; disabledForeground = (Color) value; return oldValue; - case "acceleratorForeground": oldValue = acceleratorForeground; acceleratorForeground = (Color) value; return oldValue; - case "acceleratorSelectionForeground": oldValue = acceleratorSelectionForeground; acceleratorSelectionForeground = (Color) value; return oldValue; - } - return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, menuItem, key, value ); } /** @since 2 */ @Override public Map> getStyleableInfos( JComponent c ) { - return getStyleableInfos( renderer ); + return getStyleableInfos( this, renderer ); } - static Map> getStyleableInfos( FlatMenuItemRenderer renderer ) { - Map> infos = new FlatStylingSupport.StyleableInfosMap<>(); - 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 ); + static Map> getStyleableInfos( BasicMenuItemUI ui, FlatMenuItemRenderer renderer ) { + Map> infos = FlatStylingSupport.getAnnotatedStyleableInfos( ui ); infos.putAll( renderer.getStyleableInfos() ); return infos; } 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 be04f393..477a1792 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 @@ -35,7 +35,9 @@ import javax.swing.UIManager; import javax.swing.event.MouseInputListener; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.MenuBarUI; +import javax.swing.plaf.basic.BasicMenuItemUI; import javax.swing.plaf.basic.BasicMenuUI; +import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException; import com.formdev.flatlaf.util.LoggingFacade; @@ -78,6 +80,12 @@ import com.formdev.flatlaf.util.LoggingFacade; * * @author Karl Tauber */ +@StyleableField( cls=BasicMenuItemUI.class, key="selectionBackground" ) +@StyleableField( cls=BasicMenuItemUI.class, key="selectionForeground" ) +@StyleableField( cls=BasicMenuItemUI.class, key="disabledForeground" ) +@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorForeground" ) +@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorSelectionForeground" ) + public class FlatMenuUI extends BasicMenuUI implements StyleableUI @@ -172,23 +180,13 @@ public class FlatMenuUI // ignore } - Object oldValue; - switch( key ) { - // BasicMenuItemUI - case "selectionBackground": oldValue = selectionBackground; selectionBackground = (Color) value; return oldValue; - case "selectionForeground": oldValue = selectionForeground; selectionForeground = (Color) value; return oldValue; - case "disabledForeground": oldValue = disabledForeground; disabledForeground = (Color) value; return oldValue; - case "acceleratorForeground": oldValue = acceleratorForeground; acceleratorForeground = (Color) value; return oldValue; - case "acceleratorSelectionForeground": oldValue = acceleratorSelectionForeground; acceleratorSelectionForeground = (Color) value; return oldValue; - } - return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, menuItem, key, value ); } /** @since 2 */ @Override public Map> getStyleableInfos( JComponent c ) { - return FlatMenuItemUI.getStyleableInfos( renderer ); + return FlatMenuItemUI.getStyleableInfos( this, renderer ); } @Override 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 47f7ba06..d77832e4 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 @@ -16,7 +16,6 @@ package com.formdev.flatlaf.ui; -import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.beans.PropertyChangeListener; @@ -25,7 +24,9 @@ import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.LookAndFeel; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicMenuItemUI; import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI; +import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException; import com.formdev.flatlaf.util.LoggingFacade; @@ -58,6 +59,12 @@ import com.formdev.flatlaf.util.LoggingFacade; * * @author Karl Tauber */ +@StyleableField( cls=BasicMenuItemUI.class, key="selectionBackground" ) +@StyleableField( cls=BasicMenuItemUI.class, key="selectionForeground" ) +@StyleableField( cls=BasicMenuItemUI.class, key="disabledForeground" ) +@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorForeground" ) +@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorSelectionForeground" ) + public class FlatRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI implements StyleableUI @@ -125,23 +132,13 @@ public class FlatRadioButtonMenuItemUI // ignore } - Object oldValue; - switch( key ) { - // BasicMenuItemUI - case "selectionBackground": oldValue = selectionBackground; selectionBackground = (Color) value; return oldValue; - case "selectionForeground": oldValue = selectionForeground; selectionForeground = (Color) value; return oldValue; - case "disabledForeground": oldValue = disabledForeground; disabledForeground = (Color) value; return oldValue; - case "acceleratorForeground": oldValue = acceleratorForeground; acceleratorForeground = (Color) value; return oldValue; - case "acceleratorSelectionForeground": oldValue = acceleratorSelectionForeground; acceleratorSelectionForeground = (Color) value; return oldValue; - } - return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, menuItem, key, value ); } /** @since 2 */ @Override public Map> getStyleableInfos( JComponent c ) { - return FlatMenuItemUI.getStyleableInfos( renderer ); + return FlatMenuItemUI.getStyleableInfos( this, renderer ); } @Override 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 caffbfde..8d9c8838 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 @@ -37,6 +37,7 @@ 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.StyleableField; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.UIScale; @@ -77,6 +78,12 @@ import com.formdev.flatlaf.util.UIScale; * * @author Karl Tauber */ +@StyleableField( cls=BasicScrollBarUI.class, key="track", fieldName="trackColor" ) +@StyleableField( cls=BasicScrollBarUI.class, key="thumb", fieldName="thumbColor" ) +@StyleableField( cls=BasicScrollBarUI.class, key="width", fieldName="scrollBarWidth" ) +@StyleableField( cls=BasicScrollBarUI.class, key="minimumThumbSize" ) +@StyleableField( cls=BasicScrollBarUI.class, key="maximumThumbSize" ) + public class FlatScrollBarUI extends BasicScrollBarUI implements StyleableUI @@ -246,30 +253,13 @@ public class FlatScrollBarUI /** @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 FlatStylingSupport.StyleableInfosMap<>(); - 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; + return FlatStylingSupport.getAnnotatedStyleableInfos( this ); } @Override 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 index ebe39427..148e5998 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatStylingSupport.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatStylingSupport.java @@ -18,6 +18,7 @@ package com.formdev.flatlaf.ui; import java.beans.PropertyChangeListener; import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -63,6 +64,36 @@ public class FlatStylingSupport Class type() default Void.class; } + /** + * Indicates that a field in the specified (super) class + * is intended to be used by FlatLaf styling support. + *

+ * Use this annotation, instead of {@link Styleable}, to style fields + * in superclasses, where it is not possible to use {@link Styleable}. + * + * @since 2.5 + */ + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.RUNTIME) + @Repeatable(StyleableFields.class) + public @interface StyleableField { + Class cls(); + String key(); + String fieldName() default ""; + } + + /** + * Container annotation for {@link StyleableField}. + * + * @since 2.5 + */ + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.RUNTIME) + public @interface StyleableFields { + StyleableField[] value(); + } + + /** @since 2 */ public interface StyleableUI { Map> getStyleableInfos( JComponent c ); @@ -405,26 +436,17 @@ public class FlatStylingSupport 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, convertToEnum( value, f.getType() ) ); - return oldValue; - } catch( IllegalAccessException ex ) { - throw new IllegalArgumentException( "failed to access field '" + cls.getName() + "." + fieldName + "'", ex ); - } - } + if( predicate == null || predicate.test( f ) ) + return applyToField( f, obj, value ); } catch( NoSuchFieldException ex ) { // field not found in class --> try superclass } + for( StyleableField styleableField : cls.getAnnotationsByType( StyleableField.class ) ) { + if( key.equals( styleableField.key() ) ) + return applyToField( getStyleableField( styleableField ), obj, value ); + } + cls = cls.getSuperclass(); if( cls == null ) throw new UnknownStyleException( key ); @@ -437,11 +459,40 @@ public class FlatStylingSupport } } + private static Object applyToField( Field f, Object obj, Object value ) { + if( !isValidField( f ) ) + throw new IllegalArgumentException( "field '" + f.getDeclaringClass().getName() + "." + f.getName() + "' 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, convertToEnum( value, f.getType() ) ); + return oldValue; + } catch( IllegalAccessException ex ) { + throw new IllegalArgumentException( "failed to access field '" + f.getDeclaringClass().getName() + "." + f.getName() + "'", ex ); + } + } + private static boolean isValidField( Field f ) { int modifiers = f.getModifiers(); return (modifiers & (Modifier.FINAL|Modifier.STATIC)) == 0 && !f.isSynthetic(); } + private static Field getStyleableField( StyleableField styleableField ) { + String fieldName = styleableField.fieldName(); + if( fieldName.isEmpty() ) + fieldName = styleableField.key(); + + try { + return styleableField.cls().getDeclaredField( fieldName ); + } catch( NoSuchFieldException ex ) { + throw new IllegalArgumentException( "field '" + styleableField.cls().getName() + "." + fieldName + "' not found", ex ); + } + } + /** * Applies the given value to a property of the given object. * Works only for properties that have public getter and setter methods. @@ -628,6 +679,7 @@ public class FlatStylingSupport Class cls = obj.getClass(); for(;;) { + // find fields annotated with 'Styleable' for( Field f : cls.getDeclaredFields() ) { if( !isValidField( f ) ) continue; @@ -666,6 +718,20 @@ public class FlatStylingSupport infos.put( name, type ); } + // get fields specified in 'StyleableField' annotation + for( StyleableField styleableField : cls.getAnnotationsByType( StyleableField.class ) ) { + String name = styleableField.key(); + + // for the case that the same field name is used in a class and in + // one of its superclasses, do not process field in superclass + if( processedFields.contains( name ) ) + continue; + processedFields.add( name ); + + Field f = getStyleableField( styleableField ); + infos.put( name, f.getType() ); + } + cls = cls.getSuperclass(); if( cls == null ) return;