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
This commit is contained in:
Karl Tauber
2020-10-17 15:19:39 +02:00
parent 10746a454a
commit 15718cdb46

View File

@@ -120,6 +120,8 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TabbedPane.hiddenTabsNavigation String moreTabsButton (default) or arrowButtons * @uiDefault TabbedPane.hiddenTabsNavigation String moreTabsButton (default) or arrowButtons
* @uiDefault ScrollPane.smoothScrolling boolean * @uiDefault ScrollPane.smoothScrolling boolean
* *
* @uiDefault TabbedPane.moreTabsButtonToolTipText String
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatTabbedPaneUI public class FlatTabbedPaneUI
@@ -149,7 +151,7 @@ public class FlatTabbedPaneUI
protected boolean tabSeparatorsFullHeight; protected boolean tabSeparatorsFullHeight;
protected boolean hasFullBorder; protected boolean hasFullBorder;
protected int hiddenTabsNavigation = MORE_TABS_BUTTON; private String hiddenTabsNavigationStr;
protected String moreTabsButtonToolTipText; protected String moreTabsButtonToolTipText;
@@ -207,6 +209,7 @@ public class FlatTabbedPaneUI
showTabSeparators = UIManager.getBoolean( "TabbedPane.showTabSeparators" ); showTabSeparators = UIManager.getBoolean( "TabbedPane.showTabSeparators" );
tabSeparatorsFullHeight = UIManager.getBoolean( "TabbedPane.tabSeparatorsFullHeight" ); tabSeparatorsFullHeight = UIManager.getBoolean( "TabbedPane.tabSeparatorsFullHeight" );
hasFullBorder = UIManager.getBoolean( "TabbedPane.hasFullBorder" ); hasFullBorder = UIManager.getBoolean( "TabbedPane.hasFullBorder" );
hiddenTabsNavigationStr = UIManager.getString( "TabbedPane.hiddenTabsNavigation" );
Locale l = tabPane.getLocale(); Locale l = tabPane.getLocale();
moreTabsButtonToolTipText = UIManager.getString( "TabbedPane.moreTabsButtonToolTipText", l ); moreTabsButtonToolTipText = UIManager.getString( "TabbedPane.moreTabsButtonToolTipText", l );
@@ -285,16 +288,8 @@ public class FlatTabbedPaneUI
} }
protected void installHiddenTabsNavigation() { protected void installHiddenTabsNavigation() {
// initialize here because used in installHiddenTabsNavigation() before installDefaults() was invoked if( !isScrollTabLayout() || tabViewport == null )
String hiddenTabsNavigationStr = (String) tabPane.getClientProperty( TABBED_PANE_HIDDEN_TABS_NAVIGATION ); return;
if( hiddenTabsNavigationStr == null )
hiddenTabsNavigationStr = UIManager.getString( "TabbedPane.hiddenTabsNavigation" );
hiddenTabsNavigation = parseHiddenTabsNavigation( hiddenTabsNavigationStr );
if( hiddenTabsNavigation != MORE_TABS_BUTTON ||
!isScrollTabLayout() ||
tabViewport == null )
return;
// At this point, BasicTabbedPaneUI already has installed // At this point, BasicTabbedPaneUI already has installed
// TabbedPaneScrollLayout (in super.createLayoutManager()) and // TabbedPaneScrollLayout (in super.createLayoutManager()) and
@@ -550,10 +545,9 @@ public class FlatTabbedPaneUI
return; 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) // (normally this is done by invoker, but fails in this case)
if( hiddenTabsNavigation == MORE_TABS_BUTTON && if( tabViewport != null &&
tabViewport != null &&
(tabPlacement == TOP || tabPlacement == BOTTOM) ) (tabPlacement == TOP || tabPlacement == BOTTOM) )
{ {
Rectangle viewRect = tabViewport.getViewRect(); Rectangle viewRect = tabViewport.getViewRect();
@@ -831,6 +825,13 @@ public class FlatTabbedPaneUI
return UIManager.getBoolean( "ScrollPane.smoothScrolling" ); 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 ) { protected static int parseHiddenTabsNavigation( String str ) {
if( str == null ) if( str == null )
return MORE_TABS_BUTTON; return MORE_TABS_BUTTON;
@@ -1051,12 +1052,10 @@ public class FlatTabbedPaneUI
} }
@Override @Override
public Dimension getPreferredSize() { protected void fireActionPerformed( ActionEvent event ) {
Dimension size = super.getPreferredSize(); runWithOriginalLayoutManager( () -> {
boolean horizontal = (direction == WEST || direction == EAST); super.fireActionPerformed( event );
return new Dimension( } );
horizontal ? size.width : Math.max( size.width, maxTabWidth ),
horizontal ? Math.max( size.height, maxTabHeight ) : size.height );
} }
@Override @Override
@@ -1386,13 +1385,8 @@ public class FlatTabbedPaneUI
case TABBED_PANE_SHOW_CONTENT_SEPARATOR: case TABBED_PANE_SHOW_CONTENT_SEPARATOR:
case TABBED_PANE_HAS_FULL_BORDER: case TABBED_PANE_HAS_FULL_BORDER:
case TABBED_PANE_TAB_HEIGHT: case TABBED_PANE_TAB_HEIGHT:
tabPane.revalidate();
tabPane.repaint();
break;
case TABBED_PANE_HIDDEN_TABS_NAVIGATION: case TABBED_PANE_HIDDEN_TABS_NAVIGATION:
uninstallHiddenTabsNavigation(); tabPane.revalidate();
installHiddenTabsNavigation();
tabPane.repaint(); tabPane.repaint();
break; break;
@@ -1516,20 +1510,34 @@ public class FlatTabbedPaneUI
delegate.layoutContainer( parent ); 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() ) { for( Component c : tabPane.getComponents() ) {
if( c instanceof FlatScrollableTabButton && c.isVisible() ) if( c instanceof FlatScrollableTabButton ) {
c.setVisible( false ); 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(); Rectangle bounds = tabPane.getBounds();
Insets insets = tabPane.getInsets(); Insets insets = tabPane.getInsets();
int tabPlacement = tabPane.getTabPlacement(); int tabPlacement = tabPane.getTabPlacement();
Insets tabAreaInsets = getTabAreaInsets( tabPlacement ); Insets tabAreaInsets = getTabAreaInsets( tabPlacement );
Rectangle lastRect = rects[rects.length - 1]; 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 leftToRight = tabPane.getComponentOrientation().isLeftToRight();
boolean moreTabsButtonVisible = false; boolean buttonsVisible = false;
// TabbedPaneScrollLayout adds tabAreaInsets to tab coordinates, // TabbedPaneScrollLayout adds tabAreaInsets to tab coordinates,
// but we use it to position the viewport // but we use it to position the viewport
@@ -1582,18 +1590,24 @@ public class FlatTabbedPaneUI
int txi = tx + tabAreaInsets.left; int txi = tx + tabAreaInsets.left;
int twi = tw - tabAreaInsets.left - tabAreaInsets.right; int twi = tw - tabAreaInsets.left - tabAreaInsets.right;
// layout viewport and "more tabs" button // layout viewport and buttons
int viewportWidth = twi; int viewportWidth = twi;
int tabsWidth = leftToRight int tabsWidth = leftToRight
? (lastRect.x + lastRect.width) ? (lastRect.x + lastRect.width)
: (rects[0].x + rects[0].width - lastRect.x); : (rects[0].x + rects[0].width - lastRect.x);
if( viewportWidth < tabsWidth ) { if( viewportWidth < tabsWidth ) {
// need "more tabs" button // need buttons
viewportWidth = Math.max( viewportWidth - moreButtonSize.width, 0 ); 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 ); if( useMoreButton )
tabViewport.setBounds( leftToRight ? txi : (txi + moreButtonSize.width), tyi, viewportWidth, th ); moreTabsButton.setBounds( leftToRight ? (txi + twi - buttonsWidth) : txi, tyi, moreButtonSize.width, th );
moreTabsButtonVisible = true; 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 } else
tabViewport.setBounds( txi, tyi, viewportWidth, th ); tabViewport.setBounds( txi, tyi, viewportWidth, th );
@@ -1637,21 +1651,29 @@ public class FlatTabbedPaneUI
int tyi = ty + tabAreaInsets.top; int tyi = ty + tabAreaInsets.top;
int thi = th - tabAreaInsets.top - tabAreaInsets.bottom; int thi = th - tabAreaInsets.top - tabAreaInsets.bottom;
// layout viewport and "more tabs" button // layout viewport and buttons
int viewportHeight = thi; int viewportHeight = thi;
int tabsHeight = lastRect.y + lastRect.height; int tabsHeight = lastRect.y + lastRect.height;
if( viewportHeight < tabsHeight ) { if( viewportHeight < tabsHeight ) {
// need "more tabs" button // need buttons
viewportHeight = Math.max( viewportHeight - moreButtonSize.height, 0 ); 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 ); if( useMoreButton )
moreTabsButtonVisible = true; 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 ); tabViewport.setBounds( txi, tyi, tw, viewportHeight );
} }
// show/hide "more tabs" button // show/hide buttons
moreTabsButton.setVisible( moreTabsButtonVisible ); moreTabsButton.setVisible( useMoreButton && buttonsVisible );
backwardButton.setVisible( !useMoreButton && buttonsVisible );
forwardButton.setVisible( !useMoreButton && buttonsVisible );
} }
private void shiftTabs( int sx, int sy ) { private void shiftTabs( int sx, int sy ) {