From b57e4c05653fae3a27b0118c398bf82efc218b97 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Tue, 19 Apr 2022 22:19:47 +0200 Subject: [PATCH] TabbedPane: selected tab underline color now changes depending on whether the focus is within the tab content (issue #398) --- CHANGELOG.md | 2 + .../com/formdev/flatlaf/IntelliJTheme.java | 1 + .../formdev/flatlaf/ui/FlatTabbedPaneUI.java | 103 +++++++++++++++++- .../com/formdev/flatlaf/ui/FlatUIUtils.java | 2 +- .../formdev/flatlaf/FlatDarkLaf.properties | 1 + .../formdev/flatlaf/FlatLightLaf.properties | 1 + .../flatlaf/IntelliJTheme$ThemeLaf.properties | 7 ++ .../flatlaf/ui/TestFlatStyleableInfo.java | 1 + .../formdev/flatlaf/ui/TestFlatStyling.java | 1 + .../dumps/uidefaults/FlatDarkLaf_1.8.0.txt | 1 + .../dumps/uidefaults/FlatLightLaf_1.8.0.txt | 1 + .../dumps/uidefaults/FlatTestLaf_1.8.0.txt | 1 + .../flatlaf/testing/FlatTestLaf.properties | 1 + .../flatlaf/themeeditor/FlatLafUIKeys.txt | 1 + 14 files changed, 121 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e53c400..ac9ebd16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ FlatLaf Change Log #### New features and improvements +- TabbedPane: Selected tab underline color now changes depending on whether the + focus is within the tab content. (issue #398) - IntelliJ Themes: TabbedPane now use different background color for selected tabs in all "Arc" themes, in "Hiberbee Dark" and in all "Material UI Lite" themes. diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/IntelliJTheme.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/IntelliJTheme.java index c87a7776..52107a12 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/IntelliJTheme.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/IntelliJTheme.java @@ -618,6 +618,7 @@ public class IntelliJTheme // TabbedPane uiKeyCopying.put( "TabbedPane.selectedBackground", "DefaultTabs.underlinedTabBackground" ); uiKeyCopying.put( "TabbedPane.selectedForeground", "DefaultTabs.underlinedTabForeground" ); + uiKeyCopying.put( "TabbedPane.inactiveUnderlineColor", "DefaultTabs.inactiveUnderlineColor" ); // TitlePane uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" ); 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 9a2cdda7..bbe45782 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 @@ -59,6 +59,7 @@ import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.IntConsumer; +import java.util.function.Predicate; import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; import javax.swing.Action; @@ -82,10 +83,12 @@ import javax.swing.event.ChangeListener; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.TabbedPaneUI; import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicTabbedPaneUI; import javax.swing.text.JTextComponent; import javax.swing.text.View; +import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.icons.FlatTabbedPaneCloseIcon; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; @@ -127,6 +130,7 @@ import com.formdev.flatlaf.util.UIScale; * @uiDefault TabbedPane.selectedBackground Color optional * @uiDefault TabbedPane.selectedForeground Color * @uiDefault TabbedPane.underlineColor Color + * @uiDefault TabbedPane.inactiveUnderlineColor Color * @uiDefault TabbedPane.disabledUnderlineColor Color * @uiDefault TabbedPane.hoverColor Color * @uiDefault TabbedPane.focusColor Color @@ -198,6 +202,7 @@ public class FlatTabbedPaneUI @Styleable protected Color selectedBackground; @Styleable protected Color selectedForeground; @Styleable protected Color underlineColor; + /** @since 2.2 */ @Styleable protected Color inactiveUnderlineColor; @Styleable protected Color disabledUnderlineColor; @Styleable protected Color hoverColor; @Styleable protected Color focusColor; @@ -288,6 +293,7 @@ public class FlatTabbedPaneUI super.installUI( c ); + FlatSelectedTabRepainter.install(); installStyle(); } @@ -318,6 +324,7 @@ public class FlatTabbedPaneUI selectedBackground = UIManager.getColor( "TabbedPane.selectedBackground" ); selectedForeground = UIManager.getColor( "TabbedPane.selectedForeground" ); underlineColor = UIManager.getColor( "TabbedPane.underlineColor" ); + inactiveUnderlineColor = FlatUIUtils.getUIColor( "TabbedPane.inactiveUnderlineColor", underlineColor ); disabledUnderlineColor = UIManager.getColor( "TabbedPane.disabledUnderlineColor" ); hoverColor = UIManager.getColor( "TabbedPane.hoverColor" ); focusColor = UIManager.getColor( "TabbedPane.focusColor" ); @@ -385,6 +392,7 @@ public class FlatTabbedPaneUI selectedBackground = null; selectedForeground = null; underlineColor = null; + inactiveUnderlineColor = null; disabledUnderlineColor = null; hoverColor = null; focusColor = null; @@ -733,7 +741,6 @@ public class FlatTabbedPaneUI // increase size of repaint region to include part of content border if( contentSeparatorHeight > 0 && - getTabType() == TAB_TYPE_CARD && clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, true ) ) { int sh = scale( contentSeparatorHeight ); @@ -1205,7 +1212,9 @@ public class FlatTabbedPaneUI } protected void paintTabSelection( Graphics g, int tabPlacement, int x, int y, int w, int h ) { - g.setColor( tabPane.isEnabled() ? underlineColor : disabledUnderlineColor ); + g.setColor( tabPane.isEnabled() + ? (isTabbedPaneOrChildFocused() ? underlineColor : inactiveUnderlineColor) + : disabledUnderlineColor ); // paint underline selection boolean atBottom = (getTabType() != TAB_TYPE_CARD); @@ -1236,6 +1245,23 @@ public class FlatTabbedPaneUI } } + /** @since 2.2 */ + @SuppressWarnings( "unchecked" ) + protected boolean isTabbedPaneOrChildFocused() { + KeyboardFocusManager keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + + Object value = tabPane.getClientProperty( FlatClientProperties.COMPONENT_FOCUS_OWNER ); + if( value instanceof Predicate ) { + return ((Predicate)value).test( tabPane ) && + FlatUIUtils.isInActiveWindow( tabPane, keyboardFocusManager.getActiveWindow() ); + } + + Component focusOwner = keyboardFocusManager.getPermanentFocusOwner(); + return focusOwner != null && + SwingUtilities.isDescendingFrom( focusOwner, tabPane ) && + FlatUIUtils.isInActiveWindow( focusOwner, keyboardFocusManager.getActiveWindow() ); + } + /** * Actually does nearly the same as super.paintContentBorder() but * - not using UIManager.getColor("TabbedPane.contentAreaColor") to be GUI builder friendly @@ -3341,4 +3367,77 @@ public class FlatTabbedPaneUI delegate.actionPerformed( e ); } } + + //---- class FlatSelectedTabRepainter ------------------------------------- + + private static class FlatSelectedTabRepainter + implements PropertyChangeListener//, Runnable + { + private static FlatSelectedTabRepainter instance; + + private KeyboardFocusManager keyboardFocusManager; + + static void install() { + synchronized( FlatSelectedTabRepainter.class ) { + if( instance != null ) + return; + + instance = new FlatSelectedTabRepainter(); + } + } + + FlatSelectedTabRepainter() { + keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + keyboardFocusManager.addPropertyChangeListener( this ); + } + + private void uninstall() { + synchronized( FlatSelectedTabRepainter.class ) { + if( instance == null ) + return; + + keyboardFocusManager.removePropertyChangeListener( this ); + keyboardFocusManager = null; + instance = null; + } + } + + @Override + public void propertyChange( PropertyChangeEvent e ) { + // uninstall if no longer using FlatLaf + if( !(UIManager.getLookAndFeel() instanceof FlatLaf) ) { + uninstall(); + return; + } + + switch( e.getPropertyName() ) { + case "permanentFocusOwner": + Object oldValue = e.getOldValue(); + Object newValue = e.getNewValue(); + if( oldValue instanceof Component ) + repaintSelectedTabs( (Component) oldValue ); + if( newValue instanceof Component ) + repaintSelectedTabs( (Component) newValue ); + break; + + case "activeWindow": + repaintSelectedTabs( keyboardFocusManager.getPermanentFocusOwner() ); + break; + } + } + + private void repaintSelectedTabs( Component c ) { + if( c instanceof JTabbedPane ) + repaintSelectedTab( (JTabbedPane) c ); + + while( (c = SwingUtilities.getAncestorOfClass( JTabbedPane.class, c )) != null ) + repaintSelectedTab( (JTabbedPane) c ); + } + + private void repaintSelectedTab( JTabbedPane tabbedPane ) { + TabbedPaneUI ui = tabbedPane.getUI(); + if( ui instanceof FlatTabbedPaneUI ) + ((FlatTabbedPaneUI) ui).repaintTab( tabbedPane.getSelectedIndex() ); + } + } } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java index 2af95f61..fd3ddea2 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java @@ -245,7 +245,7 @@ public class FlatUIUtils isInActiveWindow( c, keyboardFocusManager.getActiveWindow() ); } - private static boolean isInActiveWindow( Component c, Window activeWindow ) { + static boolean isInActiveWindow( Component c, Window activeWindow ) { Window window = SwingUtilities.windowForComponent( c ); return window == activeWindow || (window != null && window.getType() == Window.Type.POPUP && window.getOwner() == activeWindow); diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatDarkLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatDarkLaf.properties index 91126b86..ca5abce3 100644 --- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatDarkLaf.properties +++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatDarkLaf.properties @@ -306,6 +306,7 @@ SplitPaneDivider.draggingColor = $Component.borderColor #---- TabbedPane ---- TabbedPane.underlineColor = @accentUnderlineColor +TabbedPane.inactiveUnderlineColor = mix(@accentUnderlineColor,$TabbedPane.background,60%) TabbedPane.disabledUnderlineColor = lighten(@background,23%) TabbedPane.hoverColor = darken($TabbedPane.background,5%,derived noAutoInverse) TabbedPane.focusColor = mix(@selectionBackground,$TabbedPane.background,25%) diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLightLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLightLaf.properties index 488f991c..21f77d1a 100644 --- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLightLaf.properties +++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLightLaf.properties @@ -313,6 +313,7 @@ SplitPaneDivider.draggingColor = $Component.borderColor #---- TabbedPane ---- TabbedPane.underlineColor = @accentUnderlineColor +TabbedPane.inactiveUnderlineColor = mix(@accentUnderlineColor,$TabbedPane.background,50%) TabbedPane.disabledUnderlineColor = darken(@background,28%) TabbedPane.hoverColor = darken($TabbedPane.background,7%,derived) TabbedPane.focusColor = mix(@selectionBackground,$TabbedPane.background,10%) diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/IntelliJTheme$ThemeLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/IntelliJTheme$ThemeLaf.properties index 5a2caeb9..e68bb958 100644 --- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/IntelliJTheme$ThemeLaf.properties +++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/IntelliJTheme$ThemeLaf.properties @@ -82,6 +82,13 @@ HelpButton.hoverBorderColor = null Slider.focusedColor = fade($Component.focusColor,40%,derived) +#---- TabbedPane ---- + +# colors from JBUI.CurrentTheme.DefaultTabs.inactiveUnderlineColor() +[light]TabbedPane.inactiveUnderlineColor = #9ca7b8 +[dark]TabbedPane.inactiveUnderlineColor = #747a80 + + #---- ToggleButton ---- ToggleButton.startBackground = $ToggleButton.background diff --git a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableInfo.java b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableInfo.java index 1dd5c188..3101f125 100644 --- a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableInfo.java +++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyleableInfo.java @@ -693,6 +693,7 @@ public class TestFlatStyleableInfo "selectedBackground", Color.class, "selectedForeground", Color.class, "underlineColor", Color.class, + "inactiveUnderlineColor", Color.class, "disabledUnderlineColor", Color.class, "hoverColor", Color.class, "focusColor", Color.class, diff --git a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyling.java b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyling.java index c85be429..4f60e4c5 100644 --- a/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyling.java +++ b/flatlaf-core/src/test/java/com/formdev/flatlaf/ui/TestFlatStyling.java @@ -862,6 +862,7 @@ public class TestFlatStyling ui.applyStyle( "selectedBackground: #fff" ); ui.applyStyle( "selectedForeground: #fff" ); ui.applyStyle( "underlineColor: #fff" ); + ui.applyStyle( "inactiveUnderlineColor: #fff" ); ui.applyStyle( "disabledUnderlineColor: #fff" ); ui.applyStyle( "hoverColor: #fff" ); ui.applyStyle( "focusColor: #fff" ); diff --git a/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt b/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt index 557dc459..a588786e 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt @@ -1045,6 +1045,7 @@ TabbedPane.hasFullBorder false TabbedPane.hiddenTabsNavigation moreTabsButton TabbedPane.highlight #232324 HSL 240 1 14 javax.swing.plaf.ColorUIResource [UI] TabbedPane.hoverColor #303234 HSL 210 4 20 com.formdev.flatlaf.util.DerivedColor [UI] darken(5%) +TabbedPane.inactiveUnderlineColor #466a92 HSL 212 35 42 javax.swing.plaf.ColorUIResource [UI] TabbedPane.labelShift 1 TabbedPane.light #2f3031 HSL 210 2 19 javax.swing.plaf.ColorUIResource [UI] TabbedPane.scrollButtonsPlacement both diff --git a/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt b/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt index 7992ca66..5c7b832a 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt @@ -1050,6 +1050,7 @@ TabbedPane.hasFullBorder false TabbedPane.hiddenTabsNavigation moreTabsButton TabbedPane.highlight #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI] TabbedPane.hoverColor #e0e0e0 HSL 0 0 88 com.formdev.flatlaf.util.DerivedColor [UI] darken(7% autoInverse) +TabbedPane.inactiveUnderlineColor #97bbdc HSL 209 50 73 javax.swing.plaf.ColorUIResource [UI] TabbedPane.labelShift 1 TabbedPane.light #e1e1e1 HSL 0 0 88 javax.swing.plaf.ColorUIResource [UI] TabbedPane.scrollButtonsPlacement both diff --git a/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt b/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt index 25dd0eb2..aa5760b6 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt @@ -1060,6 +1060,7 @@ TabbedPane.hasFullBorder false TabbedPane.hiddenTabsNavigation moreTabsButton TabbedPane.highlight #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI] TabbedPane.hoverColor #eeeeee HSL 0 0 93 javax.swing.plaf.ColorUIResource [UI] +TabbedPane.inactiveUnderlineColor #ff00ff HSL 300 100 50 javax.swing.plaf.ColorUIResource [UI] TabbedPane.labelShift 1 TabbedPane.light #e3e3e3 HSL 0 0 89 javax.swing.plaf.ColorUIResource [UI] TabbedPane.scrollButtonsPlacement both diff --git a/flatlaf-testing/src/main/resources/com/formdev/flatlaf/testing/FlatTestLaf.properties b/flatlaf-testing/src/main/resources/com/formdev/flatlaf/testing/FlatTestLaf.properties index 820d5436..28e9c599 100644 --- a/flatlaf-testing/src/main/resources/com/formdev/flatlaf/testing/FlatTestLaf.properties +++ b/flatlaf-testing/src/main/resources/com/formdev/flatlaf/testing/FlatTestLaf.properties @@ -347,6 +347,7 @@ TabbedPane.disabledForeground = #777 TabbedPane.selectedBackground = #0f0 TabbedPane.selectedForeground = #00f TabbedPane.underlineColor = #ff0 +TabbedPane.inactiveUnderlineColor = #f0f TabbedPane.disabledUnderlineColor = #7a7a7a TabbedPane.hoverColor = #eee TabbedPane.focusColor = #ddd diff --git a/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt b/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt index 7c69d876..37dfb70f 100644 --- a/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt +++ b/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt @@ -821,6 +821,7 @@ TabbedPane.hasFullBorder TabbedPane.hiddenTabsNavigation TabbedPane.highlight TabbedPane.hoverColor +TabbedPane.inactiveUnderlineColor TabbedPane.labelShift TabbedPane.light TabbedPane.scrollButtonsPlacement