Styling: use annotation on UI classes for fields in Basic* classes to apply style properties (to avoid boilerplate code)

This commit is contained in:
Karl Tauber
2022-07-30 11:03:17 +02:00
parent 9b1ebd658d
commit ff00a6c0f0
7 changed files with 131 additions and 100 deletions

View File

@@ -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<String, Class<?>> getStyleableInfos( JComponent c ) {
return FlatMenuItemUI.getStyleableInfos( renderer );
return FlatMenuItemUI.getStyleableInfos( this, renderer );
}
@Override

View File

@@ -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<String, Class<?>> getStyleableInfos( JComponent c ) {
Map<String, Class<?>> 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

View File

@@ -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<String, Class<?>> getStyleableInfos( JComponent c ) {
return getStyleableInfos( renderer );
return getStyleableInfos( this, renderer );
}
static Map<String, Class<?>> getStyleableInfos( FlatMenuItemRenderer renderer ) {
Map<String, Class<?>> 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<String, Class<?>> getStyleableInfos( BasicMenuItemUI ui, FlatMenuItemRenderer renderer ) {
Map<String, Class<?>> infos = FlatStylingSupport.getAnnotatedStyleableInfos( ui );
infos.putAll( renderer.getStyleableInfos() );
return infos;
}

View File

@@ -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<String, Class<?>> getStyleableInfos( JComponent c ) {
return FlatMenuItemUI.getStyleableInfos( renderer );
return FlatMenuItemUI.getStyleableInfos( this, renderer );
}
@Override

View File

@@ -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<String, Class<?>> getStyleableInfos( JComponent c ) {
return FlatMenuItemUI.getStyleableInfos( renderer );
return FlatMenuItemUI.getStyleableInfos( this, renderer );
}
@Override

View File

@@ -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<String, Class<?>> getStyleableInfos( JComponent c ) {
Map<String, Class<?>> 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

View File

@@ -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.
* <p>
* 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<String, Class<?>> 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;