From dc92d0913ca269122218febd1104bb72c915f0c5 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Tue, 27 Oct 2020 16:29:58 +0100 Subject: [PATCH] TabbedPane: trailing component now fills all available horizontal space (PR #192) --- .../formdev/flatlaf/FlatClientProperties.java | 10 + .../formdev/flatlaf/ui/FlatTabbedPaneUI.java | 237 +++++++++++------- 2 files changed, 160 insertions(+), 87 deletions(-) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java index 6a3a2c6f..f604288a 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java @@ -353,6 +353,11 @@ public interface FlatClientProperties /** * Specifies a component that will be placed at the leading edge of the tabs area. *

+ * For top and bottom tab placement, the layed out component size will be + * the preferred component width and the tab area height.
+ * For left and right tab placement, the layed out component size will be + * the tab area width and the preferred component height. + *

* Component {@link javax.swing.JTabbedPane}
* Value type {@link java.awt.Component} */ @@ -361,6 +366,11 @@ public interface FlatClientProperties /** * Specifies a component that will be placed at the trailing edge of the tabs area. *

+ * For top and bottom tab placement, the layed out component size will be + * the available horizontal space (minimum is preferred component width) and the tab area height.
+ * For left and right tab placement, the layed out component size will be + * the tab area width and the available vertical space (minimum is preferred component height). + *

* Component {@link javax.swing.JTabbedPane}
* Value type {@link java.awt.Component} */ 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 0c3d1229..20447dae 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 @@ -529,6 +529,24 @@ public class FlatTabbedPaneUI return Math.max( tabHeight, super.calculateTabHeight( tabPlacement, tabIndex, fontHeight ) - 2 /* was added by superclass */ ); } + protected int calculateMaxExtrasWidth() { + int width = 0; + if( leadingComponent != null ) + width = Math.max( width, leadingComponent.getPreferredSize().width ); + if( trailingComponent != null ) + width = Math.max( width, trailingComponent.getPreferredSize().width ); + return width; + } + + protected int calculateMaxExtrasHeight() { + int height = 0; + if( leadingComponent != null ) + height = Math.max( height, leadingComponent.getPreferredSize().height ); + if( trailingComponent != null ) + height = Math.max( height, trailingComponent.getPreferredSize().height ); + return height; + } + @Override protected Insets getTabInsets( int tabPlacement, int tabIndex ) { Object value = getTabClientProperty( tabIndex, TABBED_PANE_TAB_INSETS ); @@ -1793,21 +1811,34 @@ public class FlatTabbedPaneUI // layout leading and trailing components in tab area if( tabPlacement == TOP || tabPlacement == BOTTOM ) { + // fix x-locations of tabs in right-to-left component orientation + if( !leftToRight ) + shiftTabs( insets.left + tabAreaInsets.right, 0 ); + + // tab area height (maxTabHeight is zero if tab count is zero) + int tabAreaHeight = (maxTabHeight > 0) + ? maxTabHeight + : Math.max( calculateMaxExtrasHeight(), scale( clientPropertyInt( tabPane, TABBED_PANE_TAB_HEIGHT, tabHeight ) ) ); + // tab area bounds int tx = insets.left; int ty = (tabPlacement == TOP) - ? insets.top - : (bounds.height - insets.bottom - maxTabHeight - tabAreaInsets.top - tabAreaInsets.bottom); + ? insets.top + tabAreaInsets.top + : (bounds.height - insets.bottom - tabAreaInsets.bottom - tabAreaHeight); int tw = bounds.width - insets.left - insets.right; - int th = maxTabHeight; - - int tyi = ty + tabAreaInsets.top; + int th = tabAreaHeight; // layout left component Container leftComponent = leftToRight ? leadingComponent : trailingComponent; if( leftComponent != null ) { - int leftWidth = leftComponent.getPreferredSize().width; - leftComponent.setBounds( tx, tyi, leftWidth, th ); + // trailing component fills all available horizontal space + int leftWidth = leftToRight + ? leftComponent.getPreferredSize().width + : (rects.length > 0 + ? rects[rects.length-1].x - tx + - (tabAreaInsets.right - trailingComponent.getPreferredSize().width) + : tw - (leadingComponent != null ? leadingComponent.getPreferredSize().width : 0)); + leftComponent.setBounds( tx, ty, leftWidth, th ); // reduce tab area bounds tx += leftWidth; @@ -1817,28 +1848,31 @@ public class FlatTabbedPaneUI // layout right component Container rightComponent = leftToRight ? trailingComponent : leadingComponent; if( rightComponent != null ) { - int rightWidth = rightComponent.getPreferredSize().width; - rightComponent.setBounds( tx + tw - rightWidth, tyi, rightWidth, th ); + // trailing component fills all available horizontal space + int rightWidth = !leftToRight + ? rightComponent.getPreferredSize().width + : (rects.length > 0 + ? tx + tw - (rects[rects.length-1].x + rects[rects.length-1].width) + - (tabAreaInsets.right - trailingComponent.getPreferredSize().width) + : tw); + rightComponent.setBounds( tx + tw - rightWidth, ty, rightWidth, th ); } + } else { // LEFT and RIGHT tab placement + // tab area width (maxTabWidth is zero if tab count is zero) + int tabAreaWidth = (maxTabWidth > 0) ? maxTabWidth : calculateMaxExtrasWidth(); - // fix x-locations of tabs in right-to-left component orientation - if( !leftToRight ) - shiftTabs( insets.left + tabAreaInsets.right, 0 ); - } else { // LEFT, RIGHT // tab area bounds int tx = (tabPlacement == LEFT) - ? insets.left - : (bounds.width - insets.right - maxTabWidth - tabAreaInsets.left - tabAreaInsets.right); + ? insets.left + tabAreaInsets.left + : (bounds.width - insets.right - tabAreaInsets.right - tabAreaWidth); int ty = insets.top; - int tw = maxTabWidth; + int tw = tabAreaWidth; int th = bounds.height - insets.top - insets.bottom; - int txi = tx + tabAreaInsets.left; - // layout top component if( leadingComponent != null ) { int topHeight = leadingComponent.getPreferredSize().height; - leadingComponent.setBounds( txi, ty, tw, topHeight ); + leadingComponent.setBounds( tx, ty, tw, topHeight ); // reduce tab area bounds ty += topHeight; @@ -1847,8 +1881,12 @@ public class FlatTabbedPaneUI // layout bottom component if( trailingComponent != null ) { - int bottomHeight = trailingComponent.getPreferredSize().height; - trailingComponent.setBounds( txi, ty + th - bottomHeight, tw, bottomHeight ); + // trailing component fills all available vertical space + int bottomHeight = (rects.length > 0) + ? ty + th - (rects[rects.length-1].y + rects[rects.length-1].height) + - (tabAreaInsets.bottom - trailingComponent.getPreferredSize().height) + : th; + trailingComponent.setBounds( tx, ty + th - bottomHeight, tw, bottomHeight ); } } } @@ -1927,18 +1965,10 @@ public class FlatTabbedPaneUI if( !useMoreButton && (backwardButton == null || forwardButton == null) ) return; // should never occur - if( rects.length == 0 ) { - moreTabsButton.setVisible( false ); - backwardButton.setVisible( false ); - forwardButton.setVisible( false ); - return; - } - Rectangle bounds = tabPane.getBounds(); Insets insets = tabPane.getInsets(); int tabPlacement = tabPane.getTabPlacement(); Insets tabAreaInsets = getTabAreaInsets( tabPlacement ); - Rectangle lastRect = rects[rects.length - 1]; Dimension moreButtonSize = useMoreButton ? moreTabsButton.getPreferredSize() : null; Dimension backwardButtonSize = useMoreButton ? null : backwardButton.getPreferredSize(); Dimension forwardButtonSize = useMoreButton ? null : forwardButton.getPreferredSize(); @@ -1961,21 +1991,32 @@ public class FlatTabbedPaneUI // layout tab area if( tabPlacement == TOP || tabPlacement == BOTTOM ) { + // tab area height (maxTabHeight is zero if tab count is zero) + int tabAreaHeight = (maxTabHeight > 0) + ? maxTabHeight + : Math.max( calculateMaxExtrasHeight(), scale( clientPropertyInt( tabPane, TABBED_PANE_TAB_HEIGHT, tabHeight ) ) ); + // tab area bounds int tx = insets.left; int ty = (tabPlacement == TOP) - ? insets.top - : (bounds.height - insets.bottom - maxTabHeight - tabAreaInsets.top - tabAreaInsets.bottom); + ? insets.top + tabAreaInsets.top + : (bounds.height - insets.bottom - tabAreaInsets.bottom - tabAreaHeight); int tw = bounds.width - insets.left - insets.right; - int th = maxTabHeight; - - int tyi = ty + tabAreaInsets.top; + int th = tabAreaHeight; // layout left component Container leftComponent = leftToRight ? leadingComponent : trailingComponent; if( leftComponent != null ) { int leftWidth = leftComponent.getPreferredSize().width; - leftComponent.setBounds( tx, tyi, leftWidth, th ); + if( !leftToRight ) { + // trailing component fills all available horizontal space + int leadingWidth = (leadingComponent != null) ? leadingComponent.getPreferredSize().width : 0; + leftWidth = Math.max( leftWidth, (rects.length > 0) + ? tw - leadingWidth - (rects[0].x + rects[0].width - rects[rects.length-1].x) + - (tabAreaInsets.left + tabAreaInsets.right) + : tw - leadingWidth ); + } + leftComponent.setBounds( tx, ty, leftWidth, th ); // reduce tab area bounds tx += leftWidth; @@ -1986,58 +2027,70 @@ public class FlatTabbedPaneUI Container rightComponent = leftToRight ? trailingComponent : leadingComponent; if( rightComponent != null ) { int rightWidth = rightComponent.getPreferredSize().width; - rightComponent.setBounds( tx + tw - rightWidth, tyi, rightWidth, th ); + if( leftToRight ) { + // trailing component fills all available horizontal space + rightWidth = Math.max( rightWidth, (rects.length > 0) + ? tw - (rects[rects.length-1].x + rects[rects.length-1].width) + - (tabAreaInsets.left + tabAreaInsets.right) + : tw ); + } + rightComponent.setBounds( tx + tw - rightWidth, ty, rightWidth, th ); // reduce tab area bounds tw -= rightWidth; } - int txi = tx + tabAreaInsets.left; - int twi = tw - tabAreaInsets.left - tabAreaInsets.right; + // layout tab viewport and buttons + if( rects.length > 0 ) { + int txi = tx + (leftToRight ? tabAreaInsets.left : tabAreaInsets.right); + int twi = tw - tabAreaInsets.left - tabAreaInsets.right; - // 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 buttons - buttonsVisible = true; - int buttonsWidth = useMoreButton ? moreButtonSize.width : (backwardButtonSize.width + forwardButtonSize.width); - viewportWidth = Math.max( viewportWidth - buttonsWidth, 0 ); + // layout viewport and buttons + int viewportWidth = twi; + Rectangle lastRect = rects[rects.length - 1]; + int tabsWidth = leftToRight + ? (lastRect.x + lastRect.width) + : (rects[0].x + rects[0].width - lastRect.x); + if( viewportWidth < tabsWidth ) { + // need buttons + buttonsVisible = true; + int buttonsWidth = useMoreButton ? moreButtonSize.width : (backwardButtonSize.width + forwardButtonSize.width); + viewportWidth = Math.max( viewportWidth - buttonsWidth, 0 ); - 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 ); + if( useMoreButton ) + moreTabsButton.setBounds( leftToRight ? (txi + twi - buttonsWidth) : txi, ty, moreButtonSize.width, th ); + else { + backwardButton.setBounds( leftToRight ? (txi + twi - buttonsWidth) : txi, ty, backwardButtonSize.width, th ); + forwardButton.setBounds( leftToRight ? (txi + twi - forwardButtonSize.width) : (txi + backwardButtonSize.width), ty, forwardButtonSize.width, th ); + } + tabViewport.setBounds( leftToRight ? txi : (txi + buttonsWidth), ty, viewportWidth, th ); + } else + tabViewport.setBounds( txi, ty, viewportWidth, th ); + + if( !leftToRight ) { + // layout viewport so that we can get correct view width below + tabViewport.doLayout(); + + // fix x-locations of tabs so that they are right-aligned in the view + shiftTabs( tabViewport.getView().getWidth() - (rects[0].x + rects[0].width), 0 ); } - tabViewport.setBounds( leftToRight ? txi : (txi + buttonsWidth), tyi, viewportWidth, th ); - } else - tabViewport.setBounds( txi, tyi, viewportWidth, th ); - - if( !leftToRight ) { - // layout viewport so that we can get correct view width below - tabViewport.doLayout(); - - // fix x-locations of tabs so that they are right-aligned in the view - shiftTabs( tabViewport.getView().getWidth() - (rects[0].x + rects[0].width), 0 ); } } else { // LEFT and RIGHT tab placement + // tab area width (maxTabWidth is zero if tab count is zero) + int tabAreaWidth = (maxTabWidth > 0) ? maxTabWidth : calculateMaxExtrasWidth(); + // tab area bounds int tx = (tabPlacement == LEFT) - ? insets.left - : (bounds.width - insets.right - maxTabWidth - tabAreaInsets.left - tabAreaInsets.right); + ? insets.left + tabAreaInsets.left + : (bounds.width - insets.right - tabAreaInsets.right - tabAreaWidth); int ty = insets.top; - int tw = maxTabWidth; + int tw = tabAreaWidth; int th = bounds.height - insets.top - insets.bottom; - int txi = tx + tabAreaInsets.left; - // layout top component if( leadingComponent != null ) { int topHeight = leadingComponent.getPreferredSize().height; - leadingComponent.setBounds( txi, ty, tw, topHeight ); + leadingComponent.setBounds( tx, ty, tw, topHeight ); // reduce tab area bounds ty += topHeight; @@ -2047,35 +2100,45 @@ public class FlatTabbedPaneUI // layout bottom component if( trailingComponent != null ) { int bottomHeight = trailingComponent.getPreferredSize().height; - trailingComponent.setBounds( txi, ty + th - bottomHeight, tw, bottomHeight ); + // trailing component fills all available vertical space + bottomHeight = Math.max( bottomHeight, (rects.length > 0) + ? th - (rects[rects.length-1].y + rects[rects.length-1].height) + - (tabAreaInsets.top + tabAreaInsets.bottom) + : th ); + trailingComponent.setBounds( tx, ty + th - bottomHeight, tw, bottomHeight ); // reduce tab area bounds th -= bottomHeight; } - int tyi = ty + tabAreaInsets.top; - int thi = th - tabAreaInsets.top - tabAreaInsets.bottom; + // layout tab viewport and buttons + if( rects.length > 0 ) { + int tyi = ty + tabAreaInsets.top; + int thi = th - tabAreaInsets.top - tabAreaInsets.bottom; - // layout viewport and buttons - int viewportHeight = thi; - int tabsHeight = lastRect.y + lastRect.height; - if( viewportHeight < tabsHeight ) { - // need buttons - buttonsVisible = true; - int buttonsHeight = useMoreButton ? moreButtonSize.height : (backwardButtonSize.height + forwardButtonSize.height); - viewportHeight = Math.max( viewportHeight - buttonsHeight, 0 ); + // layout viewport and buttons + int viewportHeight = thi; + Rectangle lastRect = rects[rects.length - 1]; + int tabsHeight = lastRect.y + lastRect.height; + if( viewportHeight < tabsHeight ) { + // need buttons + buttonsVisible = true; + int buttonsHeight = useMoreButton ? moreButtonSize.height : (backwardButtonSize.height + forwardButtonSize.height); + viewportHeight = Math.max( viewportHeight - buttonsHeight, 0 ); - 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 ); + if( useMoreButton ) + moreTabsButton.setBounds( tx, tyi + thi - buttonsHeight, tw, moreButtonSize.height ); + else { + backwardButton.setBounds( tx, tyi + thi - buttonsHeight, tw, backwardButtonSize.height ); + forwardButton.setBounds( tx, tyi + thi - forwardButtonSize.height, tw, forwardButtonSize.height ); + } } + tabViewport.setBounds( tx, tyi, tw, viewportHeight ); } - tabViewport.setBounds( txi, tyi, tw, viewportHeight ); } - // show/hide buttons + // show/hide viewport and buttons + tabViewport.setVisible( rects.length > 0 ); moreTabsButton.setVisible( useMoreButton && buttonsVisible ); backwardButton.setVisible( !useMoreButton && buttonsVisible ); forwardButton.setVisible( !useMoreButton && buttonsVisible );