Styling: support Button and ToggleButton (including border)

This commit is contained in:
Karl Tauber
2021-06-19 22:31:21 +02:00
parent ab4c9bdeda
commit 6affc70a66
6 changed files with 270 additions and 81 deletions

View File

@@ -70,25 +70,16 @@ public class FlatBorder
@Styleable protected Color disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
@Styleable protected Color focusedBorderColor = UIManager.getColor( "Component.focusedBorderColor" );
protected Color errorBorderColor = UIManager.getColor( "Component.error.borderColor" );
protected Color errorFocusedBorderColor = UIManager.getColor( "Component.error.focusedBorderColor" );
protected Color warningBorderColor = UIManager.getColor( "Component.warning.borderColor" );
protected Color warningFocusedBorderColor = UIManager.getColor( "Component.warning.focusedBorderColor" );
protected 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 TODO
*/
public Object applyStyleProperty( String key, Object value ) {
Object oldValue;
switch( key ) {
case "error.borderColor": oldValue = errorBorderColor; errorBorderColor = (Color) value; return oldValue;
case "error.focusedBorderColor": oldValue = errorFocusedBorderColor; errorFocusedBorderColor = (Color) value; return oldValue;
case "warning.borderColor": oldValue = warningBorderColor; warningBorderColor = (Color) value; return oldValue;
case "warning.focusedBorderColor": oldValue = warningFocusedBorderColor; warningFocusedBorderColor = (Color) value; return oldValue;
case "custom.borderColor": oldValue = customBorderColor; customBorderColor = (Color) value; return oldValue;
}
return FlatStyleSupport.applyToAnnotatedObject( this, key, value );
}

View File

@@ -25,6 +25,7 @@ import java.awt.Paint;
import javax.swing.AbstractButton;
import javax.swing.UIManager;
import javax.swing.plaf.UIResource;
import com.formdev.flatlaf.ui.FlatStyleSupport.Styleable;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -39,9 +40,9 @@ 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.borderWidth int
* @uiDefault Button.default.borderWidth int
* @uiDefault Button.innerFocusWidth int or float optional; defaults to Component.innerFocusWidth
@@ -54,22 +55,27 @@ 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" );
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" );
protected final int arc = UIManager.getInt( "Button.arc" );
@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" );
@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" );
@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 ) {
@@ -137,11 +143,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;

View File

@@ -30,6 +30,7 @@ import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.geom.RoundRectangle2D;
import java.beans.PropertyChangeEvent;
import java.util.Map;
import javax.swing.AbstractButton;
import javax.swing.ButtonModel;
import javax.swing.Icon;
@@ -39,11 +40,14 @@ import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;
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.ui.FlatStyleSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStyleSupport.UnknownStyleException;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -94,7 +98,7 @@ import com.formdev.flatlaf.util.UIScale;
public class FlatButtonUI
extends BasicButtonUI
{
protected int minimumWidth;
@Styleable protected int minimumWidth;
protected int iconTextGap;
protected Color background;
@@ -102,38 +106,55 @@ 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 final boolean shared;
private boolean borderShared = true;
private boolean defaults_initialized = false;
private Map<String, Object> oldStyleValues;
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, FlatStyleSupport.getStyle( c ) );
}
@Override
@@ -160,16 +181,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" );
@@ -178,6 +189,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" );
@@ -225,6 +241,55 @@ public class FlatButtonUI
b.revalidate();
b.repaint();
break;
case STYLE:
applyStyle( b, this, e.getNewValue() );
break;
}
}
private static void applyStyle( AbstractButton b, FlatButtonUI ui, Object style ) {
// unshare component UI if necessary
if( style != null && ui.shared ) {
b.updateUI();
ui = (FlatButtonUI) b.getUI();
}
ui.applyStyle( b, style );
b.revalidate();
b.repaint();
}
/**
* @since TODO
*/
protected void applyStyle( AbstractButton b, Object style ) {
oldStyleValues = FlatStyleSupport.parseAndApply( oldStyleValues, style,
(key, value) -> applyStyleProperty( b, key, value ) );
}
/**
* @since TODO
*/
protected Object applyStyleProperty( AbstractButton b, String key, Object value ) {
try {
return FlatStyleSupport.applyToAnnotatedObject( this, key, value );
} catch( UnknownStyleException ex ) {
Border border = b.getBorder();
if( border instanceof FlatBorder ) {
if( borderShared ) {
border = FlatStyleSupport.cloneBorder( border );
b.setBorder( border );
borderShared = false;
}
try {
return ((FlatBorder)border).applyStyleProperty( key, value );
} catch( UnknownStyleException ex2 ) {
// ignore
}
}
throw ex;
}
}
@@ -336,7 +401,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 )) )
{

View File

@@ -50,6 +50,7 @@ public class FlatStyleSupport
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Styleable {
boolean dot() default false;
}
/**
@@ -180,14 +181,24 @@ public class FlatStyleSupport
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 );
}
Class<?> cls = obj.getClass();
for(;;) {
try {
Field f = cls.getDeclaredField( key );
if( f.isAnnotationPresent( Styleable.class ) ) {
Field f = cls.getDeclaredField( fieldName );
Styleable styleable = f.getAnnotation( Styleable.class );
if( styleable != null && styleable.dot() == (dotIndex >= 0) ) {
if( Modifier.isFinal( f.getModifiers() ) )
throw new IllegalArgumentException( "field '" + cls.getName() + "." + key + "' is final" );
throw new IllegalArgumentException( "field '" + cls.getName() + "." + fieldName + "' is final" );
try {
// necessary to access protected fields in other packages
@@ -198,7 +209,7 @@ public class FlatStyleSupport
f.set( obj, value );
return oldValue;
} catch( IllegalAccessException ex ) {
throw new IllegalArgumentException( "failed to access field '" + cls.getName() + "." + key + "'" );
throw new IllegalArgumentException( "failed to access field '" + cls.getName() + "." + fieldName + "'" );
}
}
} catch( NoSuchFieldException ex ) {

View File

@@ -26,6 +26,7 @@ import javax.swing.JComponent;
import javax.swing.JToggleButton;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import com.formdev.flatlaf.ui.FlatStyleSupport.Styleable;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -73,17 +74,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

View File

@@ -19,9 +19,12 @@ 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.Insets;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import javax.swing.AbstractButton;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JEditorPane;
import javax.swing.JFormattedTextField;
@@ -31,6 +34,7 @@ import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.JToggleButton;
import javax.swing.UIManager;
import org.junit.jupiter.api.Test;
import com.formdev.flatlaf.icons.FlatCapsLockIcon;
@@ -70,6 +74,51 @@ public class FlatStylingTests
//---- components ---------------------------------------------------------
@Test
void button() {
FlatButtonUI ui = new FlatButtonUI( false );
// create border
UIManager.put( "Button.border", new FlatButtonBorder() );
JButton b = new JButton();
ui.installUI( b );
button( b, ui );
}
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 ) );
}
@Test
void checkBox() {
FlatCheckBoxUI ui = new FlatCheckBoxUI( false );
@@ -332,8 +381,48 @@ public class FlatStylingTests
ui.applyStyle( "focusedBackground: #fff" );
}
@Test
void toggleButton() {
FlatToggleButtonUI ui = new FlatToggleButtonUI( false );
// create border
UIManager.put( "ToggleButton.border", new FlatButtonBorder() );
JToggleButton b = new JToggleButton();
ui.installUI( b );
// 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" );
}
//---- component borders --------------------------------------------------
private void flatButtonBorder( Consumer<String> 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( "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( "arc: 6" );
}
private void flatTextBorder( Consumer<String> applyStyle ) {
flatBorder( applyStyle );
@@ -358,6 +447,30 @@ public class FlatStylingTests
//---- 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( "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( "arc", 6 );
}
@Test
void flatTextBorder() {
FlatTextBorder border = new FlatTextBorder();