Styling: support Menu, MenuItem, CheckBoxMenuItem and RadioButtonMenuItem

This commit is contained in:
Karl Tauber
2021-06-21 17:24:45 +02:00
parent 20027c2db7
commit 28fb2e2a08
9 changed files with 435 additions and 46 deletions

View File

@@ -24,6 +24,8 @@ import java.awt.geom.Path2D;
import javax.swing.AbstractButton; import javax.swing.AbstractButton;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatStyleSupport;
import com.formdev.flatlaf.ui.FlatStyleSupport.Styleable;
/** /**
* Icon for {@link javax.swing.JCheckBoxMenuItem}. * Icon for {@link javax.swing.JCheckBoxMenuItem}.
@@ -38,14 +40,21 @@ import javax.swing.UIManager;
public class FlatCheckBoxMenuItemIcon public class FlatCheckBoxMenuItemIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
protected final Color checkmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.checkmarkColor" ); @Styleable protected Color checkmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.checkmarkColor" );
protected final Color disabledCheckmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.disabledCheckmarkColor" ); @Styleable protected Color disabledCheckmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.disabledCheckmarkColor" );
protected final Color selectionForeground = UIManager.getColor( "MenuItem.selectionForeground" ); @Styleable protected Color selectionForeground = UIManager.getColor( "MenuItem.selectionForeground" );
public FlatCheckBoxMenuItemIcon() { public FlatCheckBoxMenuItemIcon() {
super( 15, 15, null ); super( 15, 15, null );
} }
/**
* @since TODO
*/
public Object applyStyleProperty( String key, Object value ) {
return FlatStyleSupport.applyToAnnotatedObject( this, key, value );
}
@Override @Override
protected void paintIcon( Component c, Graphics2D g2 ) { protected void paintIcon( Component c, Graphics2D g2 ) {
boolean selected = (c instanceof AbstractButton) && ((AbstractButton)c).isSelected(); boolean selected = (c instanceof AbstractButton) && ((AbstractButton)c).isSelected();

View File

@@ -23,7 +23,9 @@ import java.awt.Graphics2D;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import javax.swing.JMenu; import javax.swing.JMenu;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatStyleSupport;
import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.ui.FlatStyleSupport.Styleable;
/** /**
* "arrow" icon for {@link javax.swing.JMenu}. * "arrow" icon for {@link javax.swing.JMenu}.
@@ -39,22 +41,29 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatMenuArrowIcon public class FlatMenuArrowIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
protected final boolean chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) ); @Styleable protected String arrowType = UIManager.getString( "Component.arrowType" );
protected final Color arrowColor = UIManager.getColor( "Menu.icon.arrowColor" ); @Styleable protected Color arrowColor = UIManager.getColor( "Menu.icon.arrowColor" );
protected final Color disabledArrowColor = UIManager.getColor( "Menu.icon.disabledArrowColor" ); @Styleable protected Color disabledArrowColor = UIManager.getColor( "Menu.icon.disabledArrowColor" );
protected final Color selectionForeground = UIManager.getColor( "Menu.selectionForeground" ); @Styleable protected Color selectionForeground = UIManager.getColor( "Menu.selectionForeground" );
public FlatMenuArrowIcon() { public FlatMenuArrowIcon() {
super( 6, 10, null ); super( 6, 10, null );
} }
/**
* @since TODO
*/
public Object applyStyleProperty( String key, Object value ) {
return FlatStyleSupport.applyToAnnotatedObject( this, key, value );
}
@Override @Override
protected void paintIcon( Component c, Graphics2D g ) { protected void paintIcon( Component c, Graphics2D g ) {
if( !c.getComponentOrientation().isLeftToRight() ) if( !c.getComponentOrientation().isLeftToRight() )
g.rotate( Math.toRadians( 180 ), width / 2., height / 2. ); g.rotate( Math.toRadians( 180 ), width / 2., height / 2. );
g.setColor( getArrowColor( c ) ); g.setColor( getArrowColor( c ) );
if( chevron ) { if( FlatUIUtils.isChevron( arrowType ) ) {
// chevron arrow // chevron arrow
Path2D path = FlatUIUtils.createPath( false, 1,1, 5,5, 1,9 ); Path2D path = FlatUIUtils.createPath( false, 1,1, 5,5, 1,9 );
g.setStroke( new BasicStroke( 1f ) ); g.setStroke( new BasicStroke( 1f ) );

View File

@@ -18,11 +18,14 @@ package com.formdev.flatlaf.ui;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;
import java.beans.PropertyChangeListener;
import java.util.Map;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI; import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
import com.formdev.flatlaf.ui.FlatStyleSupport.UnknownStyleException;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JCheckBoxMenuItem}. * Provides the Flat LaF UI delegate for {@link javax.swing.JCheckBoxMenuItem}.
@@ -56,11 +59,19 @@ public class FlatCheckBoxMenuItemUI
extends BasicCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI
{ {
private FlatMenuItemRenderer renderer; private FlatMenuItemRenderer renderer;
private Map<String, Object> oldStyleValues;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatCheckBoxMenuItemUI(); return new FlatCheckBoxMenuItemUI();
} }
@Override
public void installUI( JComponent c ) {
super.installUI( c );
applyStyle( FlatStyleSupport.getStyle( menuItem ) );
}
@Override @Override
protected void installDefaults() { protected void installDefaults() {
super.installDefaults(); super.installDefaults();
@@ -81,6 +92,31 @@ public class FlatCheckBoxMenuItemUI
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter ); return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
} }
@Override
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
return FlatStyleSupport.createPropertyChangeListener( c, this::applyStyle, super.createPropertyChangeListener( c ) );
}
/**
* @since TODO
*/
protected void applyStyle( Object style ) {
oldStyleValues = FlatStyleSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
}
/**
* @since TODO
*/
protected Object applyStyleProperty( String key, Object value ) {
try {
return renderer.applyStyleProperty( key, value );
} catch ( UnknownStyleException ex ) {
// ignore
}
return FlatMenuItemUI.applyStyleProperty( this, key, value );
}
@Override @Override
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) { protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
return renderer.getPreferredMenuItemSize(); return renderer.getPreferredMenuItemSize();

View File

@@ -39,6 +39,10 @@ import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicHTML; import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.View; import javax.swing.text.View;
import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.icons.FlatCheckBoxMenuItemIcon;
import com.formdev.flatlaf.icons.FlatMenuArrowIcon;
import com.formdev.flatlaf.ui.FlatStyleSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStyleSupport.UnknownStyleException;
import com.formdev.flatlaf.util.DerivedColor; import com.formdev.flatlaf.util.DerivedColor;
import com.formdev.flatlaf.util.Graphics2DProxy; import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.HiDPIUtils; import com.formdev.flatlaf.util.HiDPIUtils;
@@ -57,33 +61,32 @@ import com.formdev.flatlaf.util.SystemInfo;
* @uiDefault MenuItem.underlineSelectionCheckBackground Color * @uiDefault MenuItem.underlineSelectionCheckBackground Color
* @uiDefault MenuItem.underlineSelectionColor Color * @uiDefault MenuItem.underlineSelectionColor Color
* @uiDefault MenuItem.underlineSelectionHeight int * @uiDefault MenuItem.underlineSelectionHeight int
* @uiDefault MenuItem.selectionBackground Color
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatMenuItemRenderer public class FlatMenuItemRenderer
{ {
protected final JMenuItem menuItem; protected final JMenuItem menuItem;
protected final Icon checkIcon; protected Icon checkIcon;
protected final Icon arrowIcon; protected Icon arrowIcon;
protected final Font acceleratorFont; protected final Font acceleratorFont;
protected final String acceleratorDelimiter; protected final String acceleratorDelimiter;
protected final int minimumWidth = UIManager.getInt( "MenuItem.minimumWidth" ); @Styleable protected int minimumWidth = UIManager.getInt( "MenuItem.minimumWidth" );
protected final Dimension minimumIconSize; @Styleable protected Dimension minimumIconSize;
protected final int textAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textAcceleratorGap", 28 ); @Styleable protected int textAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textAcceleratorGap", 28 );
protected final int textNoAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textNoAcceleratorGap", 6 ); @Styleable protected int textNoAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textNoAcceleratorGap", 6 );
protected final int acceleratorArrowGap = FlatUIUtils.getUIInt( "MenuItem.acceleratorArrowGap", 2 ); @Styleable protected int acceleratorArrowGap = FlatUIUtils.getUIInt( "MenuItem.acceleratorArrowGap", 2 );
protected final Color checkBackground = UIManager.getColor( "MenuItem.checkBackground" ); @Styleable protected Color checkBackground = UIManager.getColor( "MenuItem.checkBackground" );
protected final Insets checkMargins = UIManager.getInsets( "MenuItem.checkMargins" ); @Styleable protected Insets checkMargins = UIManager.getInsets( "MenuItem.checkMargins" );
protected final Color underlineSelectionBackground = UIManager.getColor( "MenuItem.underlineSelectionBackground" ); @Styleable protected Color underlineSelectionBackground = UIManager.getColor( "MenuItem.underlineSelectionBackground" );
protected final Color underlineSelectionCheckBackground = UIManager.getColor( "MenuItem.underlineSelectionCheckBackground" ); @Styleable protected Color underlineSelectionCheckBackground = UIManager.getColor( "MenuItem.underlineSelectionCheckBackground" );
protected final Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" ); @Styleable protected Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" );
protected final int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" ); @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, protected FlatMenuItemRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
Font acceleratorFont, String acceleratorDelimiter ) Font acceleratorFont, String acceleratorDelimiter )
@@ -98,6 +101,54 @@ public class FlatMenuItemRenderer
this.minimumIconSize = (minimumIconSize != null) ? minimumIconSize : new Dimension( 16, 16 ); this.minimumIconSize = (minimumIconSize != null) ? minimumIconSize : new Dimension( 16, 16 );
} }
/**
* @since TODO
*/
protected Object applyStyleProperty( String key, Object value ) {
// style icon
if( key.startsWith( "icon." ) || key.equals( "selectionForeground" ) ) {
if( iconsShared ) {
if( checkIcon instanceof FlatCheckBoxMenuItemIcon )
checkIcon = FlatStyleSupport.cloneIcon( checkIcon );
if( arrowIcon instanceof FlatMenuArrowIcon )
arrowIcon = FlatStyleSupport.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 FlatStyleSupport.applyToAnnotatedObject( this, key, value );
}
protected Dimension getPreferredMenuItemSize() { protected Dimension getPreferredMenuItemSize() {
int width = 0; int width = 0;
int height = 0; int height = 0;
@@ -254,7 +305,7 @@ debug*/
paintBackground( g, underlineSelection ? underlineSelectionBackground : selectionBackground ); paintBackground( g, underlineSelection ? underlineSelectionBackground : selectionBackground );
if( underlineSelection && isArmedOrSelected( menuItem ) ) if( underlineSelection && isArmedOrSelected( menuItem ) )
paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight ); 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 ); paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground );
paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground ); paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground );
if( !isTopLevelMenu( menuItem ) ) if( !isTopLevelMenu( menuItem ) )
@@ -301,7 +352,7 @@ debug*/
return FlatUIUtils.deriveColor( background, baseColor ); 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, // 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) // then use filled icon background to indicate selection (instead of using checkIcon)
if( menuItem.isSelected() && checkIcon != null && icon != checkIcon ) { if( menuItem.isSelected() && checkIcon != null && icon != checkIcon ) {

View File

@@ -18,11 +18,14 @@ package com.formdev.flatlaf.ui;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;
import java.beans.PropertyChangeListener;
import java.util.Map;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicMenuItemUI; import javax.swing.plaf.basic.BasicMenuItemUI;
import com.formdev.flatlaf.ui.FlatStyleSupport.UnknownStyleException;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuItem}. * Provides the Flat LaF UI delegate for {@link javax.swing.JMenuItem}.
@@ -56,11 +59,19 @@ public class FlatMenuItemUI
extends BasicMenuItemUI extends BasicMenuItemUI
{ {
private FlatMenuItemRenderer renderer; private FlatMenuItemRenderer renderer;
private Map<String, Object> oldStyleValues;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatMenuItemUI(); return new FlatMenuItemUI();
} }
@Override
public void installUI( JComponent c ) {
super.installUI( c );
applyStyle( FlatStyleSupport.getStyle( menuItem ) );
}
@Override @Override
protected void installDefaults() { protected void installDefaults() {
super.installDefaults(); super.installDefaults();
@@ -81,6 +92,44 @@ public class FlatMenuItemUI
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter ); return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
} }
@Override
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
return FlatStyleSupport.createPropertyChangeListener( c, this::applyStyle, super.createPropertyChangeListener( c ) );
}
/**
* @since TODO
*/
protected void applyStyle( Object style ) {
oldStyleValues = FlatStyleSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
}
/**
* @since TODO
*/
protected Object applyStyleProperty( String key, Object value ) {
try {
return renderer.applyStyleProperty( key, value );
} catch ( UnknownStyleException ex ) {
// ignore
}
return applyStyleProperty( this, key, value );
}
static Object applyStyleProperty( BasicMenuItemUI ui, String key, Object value ) {
switch( key ) {
case "selectionBackground":
case "selectionForeground":
case "disabledForeground":
case "acceleratorForeground":
case "acceleratorSelectionForeground":
return FlatStyleSupport.applyToField( ui, key, key, value );
default: throw new UnknownStyleException( key );
}
}
@Override @Override
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) { protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
return renderer.getPreferredMenuItemSize(); return renderer.getPreferredMenuItemSize();

View File

@@ -21,6 +21,8 @@ import java.awt.Dimension;
import java.awt.Font; import java.awt.Font;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.beans.PropertyChangeListener;
import java.util.Map;
import javax.swing.ButtonModel; import javax.swing.ButtonModel;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
@@ -31,6 +33,7 @@ import javax.swing.UIManager;
import javax.swing.event.MouseInputListener; import javax.swing.event.MouseInputListener;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicMenuUI; import javax.swing.plaf.basic.BasicMenuUI;
import com.formdev.flatlaf.ui.FlatStyleSupport.UnknownStyleException;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenu}. * Provides the Flat LaF UI delegate for {@link javax.swing.JMenu}.
@@ -75,11 +78,19 @@ public class FlatMenuUI
{ {
private Color hoverBackground; private Color hoverBackground;
private FlatMenuItemRenderer renderer; private FlatMenuItemRenderer renderer;
private Map<String, Object> oldStyleValues;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatMenuUI(); return new FlatMenuUI();
} }
@Override
public void installUI( JComponent c ) {
super.installUI( c );
applyStyle( FlatStyleSupport.getStyle( menuItem ) );
}
@Override @Override
protected void installDefaults() { protected void installDefaults() {
super.installDefaults(); super.installDefaults();
@@ -129,6 +140,31 @@ public class FlatMenuUI
}; };
} }
@Override
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
return FlatStyleSupport.createPropertyChangeListener( c, this::applyStyle, super.createPropertyChangeListener( c ) );
}
/**
* @since TODO
*/
protected void applyStyle( Object style ) {
oldStyleValues = FlatStyleSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
}
/**
* @since TODO
*/
protected Object applyStyleProperty( String key, Object value ) {
try {
return renderer.applyStyleProperty( key, value );
} catch ( UnknownStyleException ex ) {
// ignore
}
return FlatMenuItemUI.applyStyleProperty( this, key, value );
}
@Override @Override
public Dimension getMinimumSize( JComponent c ) { public Dimension getMinimumSize( JComponent c ) {
// avoid that top-level menus (in menu bar) are made smaller if horizontal space is rare // avoid that top-level menus (in menu bar) are made smaller if horizontal space is rare

View File

@@ -18,11 +18,14 @@ package com.formdev.flatlaf.ui;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;
import java.beans.PropertyChangeListener;
import java.util.Map;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI; import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI;
import com.formdev.flatlaf.ui.FlatStyleSupport.UnknownStyleException;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JRadioButtonMenuItem}. * Provides the Flat LaF UI delegate for {@link javax.swing.JRadioButtonMenuItem}.
@@ -56,11 +59,19 @@ public class FlatRadioButtonMenuItemUI
extends BasicRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI
{ {
private FlatMenuItemRenderer renderer; private FlatMenuItemRenderer renderer;
private Map<String, Object> oldStyleValues;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatRadioButtonMenuItemUI(); return new FlatRadioButtonMenuItemUI();
} }
@Override
public void installUI( JComponent c ) {
super.installUI( c );
applyStyle( FlatStyleSupport.getStyle( menuItem ) );
}
@Override @Override
protected void installDefaults() { protected void installDefaults() {
super.installDefaults(); super.installDefaults();
@@ -81,6 +92,31 @@ public class FlatRadioButtonMenuItemUI
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter ); return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
} }
@Override
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
return FlatStyleSupport.createPropertyChangeListener( c, this::applyStyle, super.createPropertyChangeListener( c ) );
}
/**
* @since TODO
*/
protected void applyStyle( Object style ) {
oldStyleValues = FlatStyleSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
}
/**
* @since TODO
*/
protected Object applyStyleProperty( String key, Object value ) {
try {
return renderer.applyStyleProperty( key, value );
} catch ( UnknownStyleException ex ) {
// ignore
}
return FlatMenuItemUI.applyStyleProperty( this, key, value );
}
@Override @Override
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) { protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
return renderer.getPreferredMenuItemSize(); return renderer.getPreferredMenuItemSize();

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.beans.PropertyChangeListener;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
@@ -26,6 +27,8 @@ import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Predicate;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.UIManager; import javax.swing.UIManager;
@@ -204,13 +207,38 @@ public class FlatStyleSupport
+ key.substring( dotIndex + 2 ); + 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<Field> predicate )
throws UnknownStyleException, IllegalArgumentException
{
Class<?> cls = obj.getClass(); Class<?> cls = obj.getClass();
for(;;) { for(;;) {
try { try {
Field f = cls.getDeclaredField( fieldName ); Field f = cls.getDeclaredField( fieldName );
Styleable styleable = f.getAnnotation( Styleable.class ); if( predicate == null || predicate.test( f ) ) {
if( styleable != null && styleable.dot() == (dotIndex >= 0) ) {
if( Modifier.isFinal( f.getModifiers() ) ) if( Modifier.isFinal( f.getModifiers() ) )
throw new IllegalArgumentException( "field '" + cls.getName() + "." + fieldName + "' is final" ); throw new IllegalArgumentException( "field '" + cls.getName() + "." + fieldName + "' is final" );
@@ -234,9 +262,11 @@ public class FlatStyleSupport
if( cls == null ) if( cls == null )
throw new UnknownStyleException( key ); throw new UnknownStyleException( key );
String superclassName = cls.getName(); if( predicate != null ) {
if( superclassName.startsWith( "java." ) || superclassName.startsWith( "javax." ) ) String superclassName = cls.getName();
throw new UnknownStyleException( key ); if( superclassName.startsWith( "java." ) || superclassName.startsWith( "javax." ) )
throw new UnknownStyleException( key );
}
} }
} }
@@ -244,6 +274,21 @@ public class FlatStyleSupport
return c.getClientProperty( FlatClientProperties.STYLE ); return c.getClientProperty( FlatClientProperties.STYLE );
} }
static PropertyChangeListener createPropertyChangeListener( JComponent c,
Consumer<Object> 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 ) { static Border cloneBorder( Border border ) {
Class<? extends Border> borderClass = border.getClass(); Class<? extends Border> borderClass = border.getClass();
try { try {

View File

@@ -23,23 +23,9 @@ import java.awt.Insets;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
import javax.swing.AbstractButton; import javax.swing.*;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JEditorPane;
import javax.swing.JFormattedTextField;
import javax.swing.JPasswordField;
import javax.swing.JRadioButton;
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 org.junit.jupiter.api.Test;
import com.formdev.flatlaf.icons.FlatCapsLockIcon; import com.formdev.flatlaf.icons.*;
import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
import com.formdev.flatlaf.icons.FlatRadioButtonIcon;
/** /**
* @author Karl Tauber * @author Karl Tauber
@@ -157,6 +143,95 @@ public class FlatStylingTests
textField( ui ); textField( ui );
} }
@Test
void menu() {
UIManager.put( "Menu.arrowIcon", new FlatMenuArrowIcon() );
UIManager.put( "Menu.checkIcon", null );
FlatMenuUI ui = new FlatMenuUI();
ui.installUI( new JMenu() );
Consumer<String> applyStyle = style -> ui.applyStyle( style );
menuItem( applyStyle );
menuItem_arrowIcon( applyStyle );
}
@Test
void menuItem() {
UIManager.put( "MenuItem.arrowIcon", new FlatMenuItemArrowIcon() );
UIManager.put( "MenuItem.checkIcon", null );
FlatMenuItemUI ui = new FlatMenuItemUI();
ui.installUI( new JMenuItem() );
Consumer<String> applyStyle = style -> ui.applyStyle( style );
menuItem( applyStyle );
menuItem_arrowIcon( applyStyle );
}
@Test
void checkBoxMenuItem() {
UIManager.put( "CheckBoxMenuItem.arrowIcon", new FlatMenuItemArrowIcon() );
UIManager.put( "CheckBoxMenuItem.checkIcon", new FlatCheckBoxMenuItemIcon() );
FlatCheckBoxMenuItemUI ui = new FlatCheckBoxMenuItemUI();
ui.installUI( new JCheckBoxMenuItem() );
Consumer<String> applyStyle = style -> ui.applyStyle( style );
menuItem( applyStyle );
menuItem_arrowIcon( applyStyle );
menuItem_checkIcon( applyStyle );
}
@Test
void radioButtonMenuItem() {
UIManager.put( "RadioButtonMenuItem.arrowIcon", new FlatMenuItemArrowIcon() );
UIManager.put( "RadioButtonMenuItem.checkIcon", new FlatRadioButtonMenuItemIcon() );
FlatRadioButtonMenuItemUI ui = new FlatRadioButtonMenuItemUI();
ui.installUI( new JRadioButtonMenuItem() );
Consumer<String> applyStyle = style -> ui.applyStyle( style );
menuItem( applyStyle );
menuItem_arrowIcon( applyStyle );
menuItem_checkIcon( applyStyle );
}
private void menuItem( Consumer<String> applyStyle ) {
applyStyle.accept( "selectionBackground: #fff" );
applyStyle.accept( "selectionForeground: #fff" );
applyStyle.accept( "disabledForeground: #fff" );
applyStyle.accept( "acceleratorForeground: #fff" );
applyStyle.accept( "acceleratorSelectionForeground: #fff" );
menuItemRenderer( applyStyle );
}
private void menuItemRenderer( Consumer<String> 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<String> applyStyle ) {
applyStyle.accept( "icon.checkmarkColor: #fff" );
applyStyle.accept( "icon.disabledCheckmarkColor: #fff" );
applyStyle.accept( "icon.selectionForeground: #fff" );
}
private void menuItem_arrowIcon( Consumer<String> applyStyle ) {
applyStyle.accept( "icon.arrowType: chevron" );
applyStyle.accept( "icon.arrowColor: #fff" );
applyStyle.accept( "icon.disabledArrowColor: #fff" );
applyStyle.accept( "selectionForeground: #fff" );
}
@Test @Test
void passwordField() { void passwordField() {
FlatPasswordFieldUI ui = new FlatPasswordFieldUI(); FlatPasswordFieldUI ui = new FlatPasswordFieldUI();
@@ -556,4 +631,47 @@ public class FlatStylingTests
icon.applyStyleProperty( "pressedBackground", Color.WHITE ); icon.applyStyleProperty( "pressedBackground", Color.WHITE );
icon.applyStyleProperty( "selectedPressedBackground", 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 );
}
} }