Styling: support TabbedPane

This commit is contained in:
Karl Tauber
2021-06-27 17:12:46 +02:00
parent 925ddaa63a
commit 2b1c55ee67
3 changed files with 198 additions and 47 deletions

View File

@@ -25,6 +25,8 @@ import java.awt.geom.Line2D;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI; import com.formdev.flatlaf.ui.FlatButtonUI;
import com.formdev.flatlaf.ui.FlatStyleSupport;
import com.formdev.flatlaf.ui.FlatStyleSupport.Styleable;
import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.ui.FlatUIUtils;
/** /**
@@ -47,39 +49,46 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatTabbedPaneCloseIcon public class FlatTabbedPaneCloseIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
protected final Dimension size = UIManager.getDimension( "TabbedPane.closeSize" ); @Styleable protected Dimension closeSize = UIManager.getDimension( "TabbedPane.closeSize" );
protected final int arc = UIManager.getInt( "TabbedPane.closeArc" ); @Styleable protected int closeArc = UIManager.getInt( "TabbedPane.closeArc" );
protected final float crossPlainSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossPlainSize", 7.5f ); @Styleable protected float closeCrossPlainSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossPlainSize", 7.5f );
protected final float crossFilledSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossFilledSize", crossPlainSize ); @Styleable protected float closeCrossFilledSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossFilledSize", closeCrossPlainSize );
protected final float closeCrossLineWidth = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossLineWidth", 1f ); @Styleable protected float closeCrossLineWidth = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossLineWidth", 1f );
protected final Color background = UIManager.getColor( "TabbedPane.closeBackground" ); @Styleable protected Color closeBackground = UIManager.getColor( "TabbedPane.closeBackground" );
protected final Color foreground = UIManager.getColor( "TabbedPane.closeForeground" ); @Styleable protected Color closeForeground = UIManager.getColor( "TabbedPane.closeForeground" );
protected final Color hoverBackground = UIManager.getColor( "TabbedPane.closeHoverBackground" ); @Styleable protected Color closeHoverBackground = UIManager.getColor( "TabbedPane.closeHoverBackground" );
protected final Color hoverForeground = UIManager.getColor( "TabbedPane.closeHoverForeground" ); @Styleable protected Color closeHoverForeground = UIManager.getColor( "TabbedPane.closeHoverForeground" );
protected final Color pressedBackground = UIManager.getColor( "TabbedPane.closePressedBackground" ); @Styleable protected Color closePressedBackground = UIManager.getColor( "TabbedPane.closePressedBackground" );
protected final Color pressedForeground = UIManager.getColor( "TabbedPane.closePressedForeground" ); @Styleable protected Color closePressedForeground = UIManager.getColor( "TabbedPane.closePressedForeground" );
public FlatTabbedPaneCloseIcon() { public FlatTabbedPaneCloseIcon() {
super( 16, 16, null ); super( 16, 16, 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 ) {
// paint background // paint background
Color bg = FlatButtonUI.buttonStateColor( c, background, null, null, hoverBackground, pressedBackground ); Color bg = FlatButtonUI.buttonStateColor( c, closeBackground, null, null, closeHoverBackground, closePressedBackground );
if( bg != null ) { if( bg != null ) {
g.setColor( FlatUIUtils.deriveColor( bg, c.getBackground() ) ); g.setColor( FlatUIUtils.deriveColor( bg, c.getBackground() ) );
g.fillRoundRect( (width - size.width) / 2, (height - size.height) / 2, g.fillRoundRect( (width - closeSize.width) / 2, (height - closeSize.height) / 2,
size.width, size.height, arc, arc ); closeSize.width, closeSize.height, closeArc, closeArc );
} }
// set cross color // set cross color
Color fg = FlatButtonUI.buttonStateColor( c, foreground, null, null, hoverForeground, pressedForeground ); Color fg = FlatButtonUI.buttonStateColor( c, closeForeground, null, null, closeHoverForeground, closePressedForeground );
g.setColor( FlatUIUtils.deriveColor( fg, c.getForeground() ) ); g.setColor( FlatUIUtils.deriveColor( fg, c.getForeground() ) );
float mx = width / 2; float mx = width / 2;
float my = height / 2; float my = height / 2;
float r = ((bg != null) ? crossFilledSize : crossPlainSize) / 2; float r = ((bg != null) ? closeCrossFilledSize : closeCrossPlainSize) / 2;
// paint cross // paint cross
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD ); Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );

View File

@@ -53,6 +53,7 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.Collections; import java.util.Collections;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.IntConsumer; import java.util.function.IntConsumer;
@@ -84,6 +85,9 @@ import javax.swing.plaf.basic.BasicTabbedPaneUI;
import javax.swing.text.JTextComponent; import javax.swing.text.JTextComponent;
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.FlatTabbedPaneCloseIcon;
import com.formdev.flatlaf.ui.FlatStyleSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStyleSupport.UnknownStyleException;
import com.formdev.flatlaf.util.Animator; import com.formdev.flatlaf.util.Animator;
import com.formdev.flatlaf.util.CubicBezierEasing; import com.formdev.flatlaf.util.CubicBezierEasing;
import com.formdev.flatlaf.util.JavaCompatibility; import com.formdev.flatlaf.util.JavaCompatibility;
@@ -101,7 +105,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TabbedPane.font Font * @uiDefault TabbedPane.font Font
* @uiDefault TabbedPane.background Color * @uiDefault TabbedPane.background Color
* @uiDefault TabbedPane.foreground Color * @uiDefault TabbedPane.foreground Color
* @uiDefault TabbedPane.shadow Color used for scroll arrows and cropped line * @uiDefault TabbedPane.shadow Color used for cropped line
* @uiDefault TabbedPane.textIconGap int * @uiDefault TabbedPane.textIconGap int
* @uiDefault TabbedPane.tabInsets Insets * @uiDefault TabbedPane.tabInsets Insets
* @uiDefault TabbedPane.selectedTabPadInsets Insets unused * @uiDefault TabbedPane.selectedTabPadInsets Insets unused
@@ -177,43 +181,43 @@ public class FlatTabbedPaneUI
private static Set<KeyStroke> focusBackwardTraversalKeys; private static Set<KeyStroke> focusBackwardTraversalKeys;
protected Color foreground; protected Color foreground;
protected Color disabledForeground; @Styleable protected Color disabledForeground;
protected Color selectedBackground; @Styleable protected Color selectedBackground;
protected Color selectedForeground; @Styleable protected Color selectedForeground;
protected Color underlineColor; @Styleable protected Color underlineColor;
protected Color disabledUnderlineColor; @Styleable protected Color disabledUnderlineColor;
protected Color hoverColor; @Styleable protected Color hoverColor;
protected Color focusColor; @Styleable protected Color focusColor;
protected Color tabSeparatorColor; @Styleable protected Color tabSeparatorColor;
protected Color contentAreaColor; @Styleable protected Color contentAreaColor;
private int textIconGapUnscaled; private int textIconGapUnscaled;
protected int minimumTabWidth; @Styleable protected int minimumTabWidth;
protected int maximumTabWidth; @Styleable protected int maximumTabWidth;
protected int tabHeight; @Styleable protected int tabHeight;
protected int tabSelectionHeight; @Styleable protected int tabSelectionHeight;
protected int contentSeparatorHeight; @Styleable protected int contentSeparatorHeight;
protected boolean showTabSeparators; @Styleable protected boolean showTabSeparators;
protected boolean tabSeparatorsFullHeight; @Styleable protected boolean tabSeparatorsFullHeight;
protected boolean hasFullBorder; @Styleable protected boolean hasFullBorder;
protected boolean tabsOpaque = true; @Styleable protected boolean tabsOpaque = true;
private int tabsPopupPolicy; @Styleable private int tabsPopupPolicy;
private int scrollButtonsPolicy; @Styleable private int scrollButtonsPolicy;
private int scrollButtonsPlacement; @Styleable private int scrollButtonsPlacement;
private int tabAreaAlignment; @Styleable private int tabAreaAlignment;
private int tabAlignment; @Styleable private int tabAlignment;
private int tabWidthMode; @Styleable private int tabWidthMode;
protected Icon closeIcon; protected Icon closeIcon;
protected String arrowType; @Styleable protected String arrowType;
protected Insets buttonInsets; @Styleable protected Insets buttonInsets;
protected int buttonArc; @Styleable protected int buttonArc;
protected Color buttonHoverBackground; @Styleable protected Color buttonHoverBackground;
protected Color buttonPressedBackground; @Styleable protected Color buttonPressedBackground;
protected String moreTabsButtonToolTipText; @Styleable protected String moreTabsButtonToolTipText;
protected JViewport tabViewport; protected JViewport tabViewport;
protected FlatWheelTabScroller wheelTabScroller; protected FlatWheelTabScroller wheelTabScroller;
@@ -231,6 +235,8 @@ public class FlatTabbedPaneUI
private boolean pressedTabClose; private boolean pressedTabClose;
private Object[] oldRenderingHints; private Object[] oldRenderingHints;
private Map<String, Object> oldStyleValues;
private boolean closeIconShared = true;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatTabbedPaneUI(); return new FlatTabbedPaneUI();
@@ -259,6 +265,8 @@ public class FlatTabbedPaneUI
buttonPressedBackground = UIManager.getColor( "TabbedPane.buttonPressedBackground" ); buttonPressedBackground = UIManager.getColor( "TabbedPane.buttonPressedBackground" );
super.installUI( c ); super.installUI( c );
applyStyle( FlatStyleSupport.getStyle( c ) );
} }
@Override @Override
@@ -313,6 +321,7 @@ public class FlatTabbedPaneUI
tabAlignment = parseAlignment( UIManager.getString( "TabbedPane.tabAlignment" ), CENTER ); tabAlignment = parseAlignment( UIManager.getString( "TabbedPane.tabAlignment" ), CENTER );
tabWidthMode = parseTabWidthMode( UIManager.getString( "TabbedPane.tabWidthMode" ) ); tabWidthMode = parseTabWidthMode( UIManager.getString( "TabbedPane.tabWidthMode" ) );
closeIcon = UIManager.getIcon( "TabbedPane.closeIcon" ); closeIcon = UIManager.getIcon( "TabbedPane.closeIcon" );
closeIconShared = true;
buttonInsets = UIManager.getInsets( "TabbedPane.buttonInsets" ); buttonInsets = UIManager.getInsets( "TabbedPane.buttonInsets" );
buttonArc = UIManager.getInt( "TabbedPane.buttonArc" ); buttonArc = UIManager.getInt( "TabbedPane.buttonArc" );
@@ -361,6 +370,8 @@ public class FlatTabbedPaneUI
buttonHoverBackground = null; buttonHoverBackground = null;
buttonPressedBackground = null; buttonPressedBackground = null;
oldStyleValues = null;
MigLayoutVisualPadding.uninstall( tabPane ); MigLayoutVisualPadding.uninstall( tabPane );
} }
@@ -558,6 +569,63 @@ public class FlatTabbedPaneUI
return new FlatScrollableTabButton( direction ); return new FlatScrollableTabButton( direction );
} }
/**
* @since TODO
*/
protected void applyStyle( Object style ) {
oldStyleValues = FlatStyleSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
// update buttons
for( Component c : tabPane.getComponents() ) {
if( c instanceof FlatTabAreaButton )
((FlatTabAreaButton)c).updateStyle();
}
}
/**
* @since TODO
*/
protected Object applyStyleProperty( String key, Object value ) {
// close icon
if( key.startsWith( "close" ) ) {
if( !(closeIcon instanceof FlatTabbedPaneCloseIcon) )
return new UnknownStyleException( key );
if( closeIconShared ) {
closeIcon = FlatStyleSupport.cloneIcon( closeIcon );
closeIconShared = false;
}
return ((FlatTabbedPaneCloseIcon)closeIcon).applyStyleProperty( key, value );
}
if( value instanceof String ) {
switch( key ) {
case "tabsPopupPolicy": value = parseTabsPopupPolicy( (String) value ); break;
case "scrollButtonsPolicy": value = parseScrollButtonsPolicy( (String) value ); break;
case "scrollButtonsPlacement": value = parseScrollButtonsPlacement( (String) value ); break;
case "tabAreaAlignment": value = parseAlignment( (String) value, LEADING ); break;
case "tabAlignment": value = parseAlignment( (String) value, CENTER ); break;
case "tabWidthMode": value = parseTabWidthMode( (String) value ); break;
}
} else {
Object oldValue = null;
switch( key ) {
// BasicTabbedPaneUI
case "tabInsets": oldValue = tabInsets; tabInsets = (Insets) value; return oldValue;
case "tabAreaInsets": oldValue = tabAreaInsets; tabAreaInsets = (Insets) value; return oldValue;
case "textIconGap":
oldValue = textIconGapUnscaled;
textIconGapUnscaled = (int) value;
textIconGap = scale( textIconGapUnscaled );
return oldValue;
}
}
return FlatStyleSupport.applyToAnnotatedObject( this, key, value );
}
protected void setRolloverTab( int x, int y ) { protected void setRolloverTab( int x, int y ) {
setRolloverTab( tabForCoordinate( tabPane, x, y ) ); setRolloverTab( tabForCoordinate( tabPane, x, y ) );
} }
@@ -1556,6 +1624,12 @@ public class FlatTabbedPaneUI
setArrowWidth( 10 ); setArrowWidth( 10 );
} }
protected void updateStyle() {
updateStyle( arrowType,
FlatTabbedPaneUI.this.foreground, FlatTabbedPaneUI.this.disabledForeground,
null, buttonHoverBackground, null, buttonPressedBackground );
}
@Override @Override
protected Color deriveBackground( Color background ) { protected Color deriveBackground( Color background ) {
return FlatUIUtils.deriveColor( background, tabPane.getBackground() ); return FlatUIUtils.deriveColor( background, tabPane.getBackground() );
@@ -2340,6 +2414,12 @@ public class FlatTabbedPaneUI
tabPane.repaint(); tabPane.repaint();
ensureSelectedTabIsVisibleLater(); ensureSelectedTabIsVisibleLater();
break; break;
case STYLE:
applyStyle( e.getNewValue() );
tabPane.revalidate();
tabPane.repaint();
break;
} }
} }

View File

@@ -523,6 +523,68 @@ public class FlatStylingTests
ui.applyStyle( "gripGap: 2" ); ui.applyStyle( "gripGap: 2" );
} }
@Test
void tabbedPane() {
FlatTabbedPaneUI ui = new FlatTabbedPaneUI();
UIManager.put( "TabbedPane.closeIcon", new FlatTabbedPaneCloseIcon() );
ui.installUI( new JTabbedPane() );
ui.applyStyle( "tabInsets: 1,2,3,4" );
ui.applyStyle( "tabAreaInsets: 1,2,3,4" );
ui.applyStyle( "disabledForeground: #fff" );
ui.applyStyle( "selectedBackground: #fff" );
ui.applyStyle( "selectedForeground: #fff" );
ui.applyStyle( "underlineColor: #fff" );
ui.applyStyle( "disabledUnderlineColor: #fff" );
ui.applyStyle( "hoverColor: #fff" );
ui.applyStyle( "focusColor: #fff" );
ui.applyStyle( "tabSeparatorColor: #fff" );
ui.applyStyle( "contentAreaColor: #fff" );
ui.applyStyle( "textIconGap: 4" );
ui.applyStyle( "minimumTabWidth: 50" );
ui.applyStyle( "maximumTabWidth: 100" );
ui.applyStyle( "tabHeight: 30" );
ui.applyStyle( "tabSelectionHeight: 3" );
ui.applyStyle( "contentSeparatorHeight: 1" );
ui.applyStyle( "showTabSeparators: false" );
ui.applyStyle( "tabSeparatorsFullHeight: false" );
ui.applyStyle( "hasFullBorder: false" );
ui.applyStyle( "tabsOpaque: false" );
ui.applyStyle( "tabsPopupPolicy: asNeeded" );
ui.applyStyle( "scrollButtonsPolicy: asNeeded" );
ui.applyStyle( "scrollButtonsPlacement: both" );
ui.applyStyle( "tabAreaAlignment: leading" );
ui.applyStyle( "tabAlignment: center" );
ui.applyStyle( "tabWidthMode: preferred" );
ui.applyStyle( "arrowType: chevron" );
ui.applyStyle( "buttonInsets: 1,2,3,4" );
ui.applyStyle( "buttonArc: 3" );
ui.applyStyle( "buttonHoverBackground: #fff" );
ui.applyStyle( "buttonPressedBackground: #fff" );
ui.applyStyle( "moreTabsButtonToolTipText: Gimme more" );
// FlatTabbedPaneCloseIcon
ui.applyStyle( "closeSize: 16,16" );
ui.applyStyle( "closeArc: 4" );
ui.applyStyle( "closeCrossPlainSize: {float}7.5" );
ui.applyStyle( "closeCrossFilledSize: {float}7.5" );
ui.applyStyle( "closeCrossLineWidth: {float}1" );
ui.applyStyle( "closeBackground: #fff" );
ui.applyStyle( "closeForeground: #fff" );
ui.applyStyle( "closeHoverBackground: #fff" );
ui.applyStyle( "closeHoverForeground: #fff" );
ui.applyStyle( "closePressedBackground: #fff" );
ui.applyStyle( "closePressedForeground: #fff" );
}
@Test @Test
void table() { void table() {
FlatTableUI ui = new FlatTableUI(); FlatTableUI ui = new FlatTableUI();