From 15718cdb4662e3e1f3dec236035c2f6864a80a34 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Sat, 17 Oct 2020 15:19:39 +0200 Subject: [PATCH] TabbedPane: support adding custom components to left and right sides of tabs area if scroll backward/foreward buttons are used (issue #40) this also fixes some minor layout issues when using tabAreaInsets and arrow buttons --- .../formdev/flatlaf/ui/FlatTabbedPaneUI.java | 110 +++++++++++------- 1 file changed, 66 insertions(+), 44 deletions(-) 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 e7104fde..d3bb380c 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 @@ -120,6 +120,8 @@ import com.formdev.flatlaf.util.UIScale; * @uiDefault TabbedPane.hiddenTabsNavigation String moreTabsButton (default) or arrowButtons * @uiDefault ScrollPane.smoothScrolling boolean * + * @uiDefault TabbedPane.moreTabsButtonToolTipText String + * * @author Karl Tauber */ public class FlatTabbedPaneUI @@ -149,7 +151,7 @@ public class FlatTabbedPaneUI protected boolean tabSeparatorsFullHeight; protected boolean hasFullBorder; - protected int hiddenTabsNavigation = MORE_TABS_BUTTON; + private String hiddenTabsNavigationStr; protected String moreTabsButtonToolTipText; @@ -207,6 +209,7 @@ public class FlatTabbedPaneUI showTabSeparators = UIManager.getBoolean( "TabbedPane.showTabSeparators" ); tabSeparatorsFullHeight = UIManager.getBoolean( "TabbedPane.tabSeparatorsFullHeight" ); hasFullBorder = UIManager.getBoolean( "TabbedPane.hasFullBorder" ); + hiddenTabsNavigationStr = UIManager.getString( "TabbedPane.hiddenTabsNavigation" ); Locale l = tabPane.getLocale(); moreTabsButtonToolTipText = UIManager.getString( "TabbedPane.moreTabsButtonToolTipText", l ); @@ -285,16 +288,8 @@ public class FlatTabbedPaneUI } protected void installHiddenTabsNavigation() { - // initialize here because used in installHiddenTabsNavigation() before installDefaults() was invoked - String hiddenTabsNavigationStr = (String) tabPane.getClientProperty( TABBED_PANE_HIDDEN_TABS_NAVIGATION ); - if( hiddenTabsNavigationStr == null ) - hiddenTabsNavigationStr = UIManager.getString( "TabbedPane.hiddenTabsNavigation" ); - hiddenTabsNavigation = parseHiddenTabsNavigation( hiddenTabsNavigationStr ); - - if( hiddenTabsNavigation != MORE_TABS_BUTTON || - !isScrollTabLayout() || - tabViewport == null ) - return; + if( !isScrollTabLayout() || tabViewport == null ) + return; // At this point, BasicTabbedPaneUI already has installed // TabbedPaneScrollLayout (in super.createLayoutManager()) and @@ -550,10 +545,9 @@ public class FlatTabbedPaneUI return; } - // clip title if "more tabs" button is used + // clip title if our layout manager is used // (normally this is done by invoker, but fails in this case) - if( hiddenTabsNavigation == MORE_TABS_BUTTON && - tabViewport != null && + if( tabViewport != null && (tabPlacement == TOP || tabPlacement == BOTTOM) ) { Rectangle viewRect = tabViewport.getViewRect(); @@ -831,6 +825,13 @@ public class FlatTabbedPaneUI return UIManager.getBoolean( "ScrollPane.smoothScrolling" ); } + protected int getHiddenTabsNavigation() { + String hiddenTabsNavigationStr = (String) tabPane.getClientProperty( TABBED_PANE_HIDDEN_TABS_NAVIGATION ); + if( hiddenTabsNavigationStr == null ) + hiddenTabsNavigationStr = this.hiddenTabsNavigationStr; + return parseHiddenTabsNavigation( hiddenTabsNavigationStr ); + } + protected static int parseHiddenTabsNavigation( String str ) { if( str == null ) return MORE_TABS_BUTTON; @@ -1051,12 +1052,10 @@ public class FlatTabbedPaneUI } @Override - public Dimension getPreferredSize() { - Dimension size = super.getPreferredSize(); - boolean horizontal = (direction == WEST || direction == EAST); - return new Dimension( - horizontal ? size.width : Math.max( size.width, maxTabWidth ), - horizontal ? Math.max( size.height, maxTabHeight ) : size.height ); + protected void fireActionPerformed( ActionEvent event ) { + runWithOriginalLayoutManager( () -> { + super.fireActionPerformed( event ); + } ); } @Override @@ -1386,13 +1385,8 @@ public class FlatTabbedPaneUI case TABBED_PANE_SHOW_CONTENT_SEPARATOR: case TABBED_PANE_HAS_FULL_BORDER: case TABBED_PANE_TAB_HEIGHT: - tabPane.revalidate(); - tabPane.repaint(); - break; - case TABBED_PANE_HIDDEN_TABS_NAVIGATION: - uninstallHiddenTabsNavigation(); - installHiddenTabsNavigation(); + tabPane.revalidate(); tabPane.repaint(); break; @@ -1516,20 +1510,34 @@ public class FlatTabbedPaneUI delegate.layoutContainer( parent ); } ); - // hide scroll buttons + boolean useMoreButton = (getHiddenTabsNavigation() == MORE_TABS_BUTTON); + + // find backward/forward scroll buttons + JButton backwardButton = null; + JButton forwardButton = null; for( Component c : tabPane.getComponents() ) { - if( c instanceof FlatScrollableTabButton && c.isVisible() ) - c.setVisible( false ); + if( c instanceof FlatScrollableTabButton ) { + int direction = ((FlatScrollableTabButton)c).getDirection(); + if( direction == WEST || direction == NORTH ) + backwardButton = (JButton) c; + else if( direction == EAST || direction == SOUTH ) + forwardButton = (JButton) c; + } } + if( !useMoreButton && (backwardButton == null || forwardButton == null) ) + return; // should never occur + Rectangle bounds = tabPane.getBounds(); Insets insets = tabPane.getInsets(); int tabPlacement = tabPane.getTabPlacement(); Insets tabAreaInsets = getTabAreaInsets( tabPlacement ); Rectangle lastRect = rects[rects.length - 1]; - Dimension moreButtonSize = moreTabsButton.getPreferredSize(); + Dimension moreButtonSize = useMoreButton ? moreTabsButton.getPreferredSize() : null; + Dimension backwardButtonSize = useMoreButton ? null : backwardButton.getPreferredSize(); + Dimension forwardButtonSize = useMoreButton ? null : forwardButton.getPreferredSize(); boolean leftToRight = tabPane.getComponentOrientation().isLeftToRight(); - boolean moreTabsButtonVisible = false; + boolean buttonsVisible = false; // TabbedPaneScrollLayout adds tabAreaInsets to tab coordinates, // but we use it to position the viewport @@ -1582,18 +1590,24 @@ public class FlatTabbedPaneUI int txi = tx + tabAreaInsets.left; int twi = tw - tabAreaInsets.left - tabAreaInsets.right; - // layout viewport and "more tabs" button + // layout viewport and buttons int viewportWidth = twi; int tabsWidth = leftToRight ? (lastRect.x + lastRect.width) : (rects[0].x + rects[0].width - lastRect.x); if( viewportWidth < tabsWidth ) { - // need "more tabs" button - viewportWidth = Math.max( viewportWidth - moreButtonSize.width, 0 ); + // need buttons + buttonsVisible = true; + int buttonsWidth = useMoreButton ? moreButtonSize.width : (backwardButtonSize.width + forwardButtonSize.width); + viewportWidth = Math.max( viewportWidth - buttonsWidth, 0 ); - moreTabsButton.setBounds( leftToRight ? (txi + twi - moreButtonSize.width) : txi, tyi, moreButtonSize.width, th ); - tabViewport.setBounds( leftToRight ? txi : (txi + moreButtonSize.width), tyi, viewportWidth, th ); - moreTabsButtonVisible = true; + if( useMoreButton ) + moreTabsButton.setBounds( leftToRight ? (txi + twi - buttonsWidth) : txi, tyi, moreButtonSize.width, th ); + else { + backwardButton.setBounds( leftToRight ? (txi + twi - buttonsWidth) : txi, tyi, backwardButtonSize.width, th ); + forwardButton.setBounds( leftToRight ? (txi + twi - forwardButtonSize.width) : (txi + backwardButtonSize.width), tyi, forwardButtonSize.width, th ); + } + tabViewport.setBounds( leftToRight ? txi : (txi + buttonsWidth), tyi, viewportWidth, th ); } else tabViewport.setBounds( txi, tyi, viewportWidth, th ); @@ -1637,21 +1651,29 @@ public class FlatTabbedPaneUI int tyi = ty + tabAreaInsets.top; int thi = th - tabAreaInsets.top - tabAreaInsets.bottom; - // layout viewport and "more tabs" button + // layout viewport and buttons int viewportHeight = thi; int tabsHeight = lastRect.y + lastRect.height; if( viewportHeight < tabsHeight ) { - // need "more tabs" button - viewportHeight = Math.max( viewportHeight - moreButtonSize.height, 0 ); + // need buttons + buttonsVisible = true; + int buttonsHeight = useMoreButton ? moreButtonSize.height : (backwardButtonSize.height + forwardButtonSize.height); + viewportHeight = Math.max( viewportHeight - buttonsHeight, 0 ); - moreTabsButton.setBounds( txi, tyi + thi - moreButtonSize.height, tw, moreButtonSize.height ); - moreTabsButtonVisible = true; + if( useMoreButton ) + moreTabsButton.setBounds( txi, tyi + thi - buttonsHeight, tw, moreButtonSize.height ); + else { + backwardButton.setBounds( txi, tyi + thi - buttonsHeight, tw, backwardButtonSize.height ); + forwardButton.setBounds( txi, tyi + thi - forwardButtonSize.height, tw, forwardButtonSize.height ); + } } tabViewport.setBounds( txi, tyi, tw, viewportHeight ); } - // show/hide "more tabs" button - moreTabsButton.setVisible( moreTabsButtonVisible ); + // show/hide buttons + moreTabsButton.setVisible( useMoreButton && buttonsVisible ); + backwardButton.setVisible( !useMoreButton && buttonsVisible ); + forwardButton.setVisible( !useMoreButton && buttonsVisible ); } private void shiftTabs( int sx, int sy ) {