diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTabbedPaneCloseIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTabbedPaneCloseIcon.java index 39e90587..006fd13f 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTabbedPaneCloseIcon.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatTabbedPaneCloseIcon.java @@ -25,6 +25,8 @@ import java.awt.geom.Line2D; import java.awt.geom.Path2D; import javax.swing.UIManager; 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; /** @@ -47,39 +49,46 @@ import com.formdev.flatlaf.ui.FlatUIUtils; public class FlatTabbedPaneCloseIcon extends FlatAbstractIcon { - protected final Dimension size = UIManager.getDimension( "TabbedPane.closeSize" ); - protected final int arc = UIManager.getInt( "TabbedPane.closeArc" ); - protected final float crossPlainSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossPlainSize", 7.5f ); - protected final float crossFilledSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossFilledSize", crossPlainSize ); - protected final float closeCrossLineWidth = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossLineWidth", 1f ); - protected final Color background = UIManager.getColor( "TabbedPane.closeBackground" ); - protected final Color foreground = UIManager.getColor( "TabbedPane.closeForeground" ); - protected final Color hoverBackground = UIManager.getColor( "TabbedPane.closeHoverBackground" ); - protected final Color hoverForeground = UIManager.getColor( "TabbedPane.closeHoverForeground" ); - protected final Color pressedBackground = UIManager.getColor( "TabbedPane.closePressedBackground" ); - protected final Color pressedForeground = UIManager.getColor( "TabbedPane.closePressedForeground" ); + @Styleable protected Dimension closeSize = UIManager.getDimension( "TabbedPane.closeSize" ); + @Styleable protected int closeArc = UIManager.getInt( "TabbedPane.closeArc" ); + @Styleable protected float closeCrossPlainSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossPlainSize", 7.5f ); + @Styleable protected float closeCrossFilledSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossFilledSize", closeCrossPlainSize ); + @Styleable protected float closeCrossLineWidth = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossLineWidth", 1f ); + @Styleable protected Color closeBackground = UIManager.getColor( "TabbedPane.closeBackground" ); + @Styleable protected Color closeForeground = UIManager.getColor( "TabbedPane.closeForeground" ); + @Styleable protected Color closeHoverBackground = UIManager.getColor( "TabbedPane.closeHoverBackground" ); + @Styleable protected Color closeHoverForeground = UIManager.getColor( "TabbedPane.closeHoverForeground" ); + @Styleable protected Color closePressedBackground = UIManager.getColor( "TabbedPane.closePressedBackground" ); + @Styleable protected Color closePressedForeground = UIManager.getColor( "TabbedPane.closePressedForeground" ); public FlatTabbedPaneCloseIcon() { super( 16, 16, null ); } + /** + * @since TODO + */ + public Object applyStyleProperty( String key, Object value ) { + return FlatStyleSupport.applyToAnnotatedObject( this, key, value ); + } + @Override protected void paintIcon( Component c, Graphics2D g ) { // 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 ) { g.setColor( FlatUIUtils.deriveColor( bg, c.getBackground() ) ); - g.fillRoundRect( (width - size.width) / 2, (height - size.height) / 2, - size.width, size.height, arc, arc ); + g.fillRoundRect( (width - closeSize.width) / 2, (height - closeSize.height) / 2, + closeSize.width, closeSize.height, closeArc, closeArc ); } // 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() ) ); float mx = width / 2; float my = height / 2; - float r = ((bg != null) ? crossFilledSize : crossPlainSize) / 2; + float r = ((bg != null) ? closeCrossFilledSize : closeCrossPlainSize) / 2; // paint cross Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD ); diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTabbedPaneUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTabbedPaneUI.java index c4f79435..ee884a8b 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTabbedPaneUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTabbedPaneUI.java @@ -53,6 +53,7 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Collections; import java.util.Locale; +import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.IntConsumer; @@ -84,6 +85,9 @@ import javax.swing.plaf.basic.BasicTabbedPaneUI; import javax.swing.text.JTextComponent; import javax.swing.text.View; 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.CubicBezierEasing; import com.formdev.flatlaf.util.JavaCompatibility; @@ -101,7 +105,7 @@ import com.formdev.flatlaf.util.UIScale; * @uiDefault TabbedPane.font Font * @uiDefault TabbedPane.background 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.tabInsets Insets * @uiDefault TabbedPane.selectedTabPadInsets Insets unused @@ -177,43 +181,43 @@ public class FlatTabbedPaneUI private static Set focusBackwardTraversalKeys; protected Color foreground; - protected Color disabledForeground; - protected Color selectedBackground; - protected Color selectedForeground; - protected Color underlineColor; - protected Color disabledUnderlineColor; - protected Color hoverColor; - protected Color focusColor; - protected Color tabSeparatorColor; - protected Color contentAreaColor; + @Styleable protected Color disabledForeground; + @Styleable protected Color selectedBackground; + @Styleable protected Color selectedForeground; + @Styleable protected Color underlineColor; + @Styleable protected Color disabledUnderlineColor; + @Styleable protected Color hoverColor; + @Styleable protected Color focusColor; + @Styleable protected Color tabSeparatorColor; + @Styleable protected Color contentAreaColor; private int textIconGapUnscaled; - protected int minimumTabWidth; - protected int maximumTabWidth; - protected int tabHeight; - protected int tabSelectionHeight; - protected int contentSeparatorHeight; - protected boolean showTabSeparators; - protected boolean tabSeparatorsFullHeight; - protected boolean hasFullBorder; - protected boolean tabsOpaque = true; + @Styleable protected int minimumTabWidth; + @Styleable protected int maximumTabWidth; + @Styleable protected int tabHeight; + @Styleable protected int tabSelectionHeight; + @Styleable protected int contentSeparatorHeight; + @Styleable protected boolean showTabSeparators; + @Styleable protected boolean tabSeparatorsFullHeight; + @Styleable protected boolean hasFullBorder; + @Styleable protected boolean tabsOpaque = true; - private int tabsPopupPolicy; - private int scrollButtonsPolicy; - private int scrollButtonsPlacement; + @Styleable private int tabsPopupPolicy; + @Styleable private int scrollButtonsPolicy; + @Styleable private int scrollButtonsPlacement; - private int tabAreaAlignment; - private int tabAlignment; - private int tabWidthMode; + @Styleable private int tabAreaAlignment; + @Styleable private int tabAlignment; + @Styleable private int tabWidthMode; protected Icon closeIcon; - protected String arrowType; - protected Insets buttonInsets; - protected int buttonArc; - protected Color buttonHoverBackground; - protected Color buttonPressedBackground; + @Styleable protected String arrowType; + @Styleable protected Insets buttonInsets; + @Styleable protected int buttonArc; + @Styleable protected Color buttonHoverBackground; + @Styleable protected Color buttonPressedBackground; - protected String moreTabsButtonToolTipText; + @Styleable protected String moreTabsButtonToolTipText; protected JViewport tabViewport; protected FlatWheelTabScroller wheelTabScroller; @@ -231,6 +235,8 @@ public class FlatTabbedPaneUI private boolean pressedTabClose; private Object[] oldRenderingHints; + private Map oldStyleValues; + private boolean closeIconShared = true; public static ComponentUI createUI( JComponent c ) { return new FlatTabbedPaneUI(); @@ -259,6 +265,8 @@ public class FlatTabbedPaneUI buttonPressedBackground = UIManager.getColor( "TabbedPane.buttonPressedBackground" ); super.installUI( c ); + + applyStyle( FlatStyleSupport.getStyle( c ) ); } @Override @@ -313,6 +321,7 @@ public class FlatTabbedPaneUI tabAlignment = parseAlignment( UIManager.getString( "TabbedPane.tabAlignment" ), CENTER ); tabWidthMode = parseTabWidthMode( UIManager.getString( "TabbedPane.tabWidthMode" ) ); closeIcon = UIManager.getIcon( "TabbedPane.closeIcon" ); + closeIconShared = true; buttonInsets = UIManager.getInsets( "TabbedPane.buttonInsets" ); buttonArc = UIManager.getInt( "TabbedPane.buttonArc" ); @@ -361,6 +370,8 @@ public class FlatTabbedPaneUI buttonHoverBackground = null; buttonPressedBackground = null; + oldStyleValues = null; + MigLayoutVisualPadding.uninstall( tabPane ); } @@ -558,6 +569,63 @@ public class FlatTabbedPaneUI 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 ) { setRolloverTab( tabForCoordinate( tabPane, x, y ) ); } @@ -1556,6 +1624,12 @@ public class FlatTabbedPaneUI setArrowWidth( 10 ); } + protected void updateStyle() { + updateStyle( arrowType, + FlatTabbedPaneUI.this.foreground, FlatTabbedPaneUI.this.disabledForeground, + null, buttonHoverBackground, null, buttonPressedBackground ); + } + @Override protected Color deriveBackground( Color background ) { return FlatUIUtils.deriveColor( background, tabPane.getBackground() ); @@ -2340,6 +2414,12 @@ public class FlatTabbedPaneUI tabPane.repaint(); ensureSelectedTabIsVisibleLater(); break; + + case STYLE: + applyStyle( e.getNewValue() ); + tabPane.revalidate(); + tabPane.repaint(); + break; } } diff --git a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/FlatStylingTests.java b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/FlatStylingTests.java index a1da30a9..9bddda13 100644 --- a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/FlatStylingTests.java +++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/FlatStylingTests.java @@ -523,6 +523,68 @@ public class FlatStylingTests 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 void table() { FlatTableUI ui = new FlatTableUI();