Styling: use annotation on fields to apply style properties (to avoid boilerplate code)

This commit is contained in:
Karl Tauber
2021-06-16 21:21:33 +02:00
parent db56486506
commit e0bc93371e
3 changed files with 90 additions and 46 deletions

View File

@@ -36,6 +36,7 @@ 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.FlatStyleSupport.Styleable;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -62,7 +63,7 @@ public class FlatRadioButtonUI
extends BasicRadioButtonUI
{
protected int iconTextGap;
protected Color disabledText;
@Styleable protected Color disabledText;
private Color defaultBackground;
@@ -158,15 +159,9 @@ public class FlatRadioButtonUI
* @since TODO
*/
protected Object applyStyleProperty( String key, Object value ) {
Object oldValue;
switch( key ) {
case "disabledText": oldValue = disabledText; disabledText = (Color) value; break;
//TODO style icon
//TODO style icon
default: throw new IllegalArgumentException( "unknown style '" + key + "'" );
}
return oldValue;
return FlatStyleSupport.applyToAnnotatedObject( this, key, value );
}
private static Insets tempInsets = new Insets( 0, 0, 0, 0 );

View File

@@ -37,6 +37,7 @@ import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicSliderUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStyleSupport.Styleable;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.UIScale;
@@ -78,23 +79,23 @@ import com.formdev.flatlaf.util.UIScale;
public class FlatSliderUI
extends BasicSliderUI
{
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;
protected Color tickColor;
@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;
@@ -209,28 +210,7 @@ public class FlatSliderUI
* @since TODO
*/
protected Object applyStyleProperty( String key, Object value ) {
Object oldValue;
switch( key ) {
case "trackWidth": oldValue = trackWidth; trackWidth = (int) value; break;
case "thumbSize": oldValue = thumbSize; thumbSize = (Dimension) value; break;
case "focusWidth": oldValue = focusWidth; focusWidth = (int) value; break;
case "trackValueColor": oldValue = trackValueColor; trackValueColor = (Color) value; break;
case "trackColor": oldValue = trackColor; trackColor = (Color) value; break;
case "thumbColor": oldValue = thumbColor; thumbColor = (Color) value; break;
case "thumbBorderColor": oldValue = thumbBorderColor; thumbBorderColor = (Color) value; break;
case "focusedColor": oldValue = focusedColor; focusedColor = (Color) value; break;
case "focusedThumbBorderColor": oldValue = focusedThumbBorderColor; focusedThumbBorderColor = (Color) value; break;
case "hoverThumbColor": oldValue = hoverThumbColor; hoverThumbColor = (Color) value; break;
case "pressedThumbColor": oldValue = pressedThumbColor; pressedThumbColor = (Color) value; break;
case "disabledTrackColor": oldValue = disabledTrackColor; disabledTrackColor = (Color) value; break;
case "disabledThumbColor": oldValue = disabledThumbColor; disabledThumbColor = (Color) value; break;
case "disabledThumbBorderColor": oldValue = disabledThumbBorderColor; disabledThumbBorderColor = (Color) value; break;
case "tickColor": oldValue = tickColor; tickColor = (Color) value; break;
default: throw new IllegalArgumentException( "unknown style '" + key + "'" );
}
return oldValue;
return FlatStyleSupport.applyToAnnotatedObject( this, key, value );
}
@Override

View File

@@ -16,6 +16,12 @@
package com.formdev.flatlaf.ui;
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.Modifier;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -34,6 +40,16 @@ import com.formdev.flatlaf.util.StringUtils;
*/
public class FlatStyleSupport
{
/**
* Indicates that a field is intended to be used by FlatLaf styling support.
* <p>
* <strong>Do not rename fields annotated with this annotation.</strong>
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Styleable {
}
/**
* Parses styles in CSS syntax ("key1: value1; key2: value2; ..."),
* converts the value strings into binary and invokes the given function
@@ -146,6 +162,59 @@ public class FlatStyleSupport
return FlatLaf.parseDefaultsValue( key, value );
}
/**
* Applies the given value to an annotated field of the given object.
* The field must be annotated with {@link Styleable}.
*
* @param object the object
* @param key the name of the field
* @param value the new value
* @return the old value of the field
* @throws IllegalArgumentException if object does not have a annotated field with given name
* or if value type does not fit to expected type 
*/
public static Object applyToAnnotatedObject( Object obj, String key, Object value )
throws IllegalArgumentException
{
Class<?> cls = obj.getClass();
for(;;) {
try {
Field f = cls.getDeclaredField( key );
if( f.isAnnotationPresent( Styleable.class ) ) {
if( Modifier.isFinal( f.getModifiers() ) )
throw new IllegalArgumentException( "field '" + cls.getName() + "." + key + "' is final" );
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() + "." + key + "'" );
}
}
} catch( NoSuchFieldException ex ) {
// field not found in class --> try superclass
}
cls = cls.getSuperclass();
if( cls == null )
throw newUnknownStyleException( key );
String superclassName = cls.getName();
if( superclassName.startsWith( "java." ) || superclassName.startsWith( "javax." ) )
throw newUnknownStyleException( key );
}
}
public static IllegalArgumentException newUnknownStyleException( String key ) {
return new IllegalArgumentException( "unknown style '" + key + "'" );
}
public static Object getStyle( JComponent c ) {
return c.getClientProperty( FlatClientProperties.COMPONENT_STYLE );
}