mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-11 06:27:13 -06:00
TabbedPane: support minimum and maximum tab widths
This commit is contained in:
@@ -19,6 +19,8 @@ FlatLaf Change Log
|
||||
`JTabbedPane.trailingComponent` to a `java.awt.Component`) (PR #192; issue
|
||||
#40)
|
||||
- TabbedPane: Support closable tabs. (PR #193; issues #31 and #40)
|
||||
- TabbedPane: Support minimum or maximum tab widths. (set client property
|
||||
`JTabbedPane.minimumTabWidth` or `JTabbedPane.maximumTabWidth` to an integer)
|
||||
- Support painting separator line between window title and content (use UI value
|
||||
`TitlePane.borderColor`). (issue #184)
|
||||
- Extras: `FlatSVGIcon` now allows specifying icon width and height in
|
||||
|
||||
@@ -246,6 +246,27 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String TABBED_PANE_HAS_FULL_BORDER = "JTabbedPane.hasFullBorder";
|
||||
|
||||
/**
|
||||
* Specifies the minimum width of a tab.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Integer}
|
||||
*/
|
||||
String TABBED_PANE_MINIMUM_TAB_WIDTH = "JTabbedPane.minimumTabWidth";
|
||||
|
||||
/**
|
||||
* Specifies the maximum width of a tab.
|
||||
* <p>
|
||||
* Applied only if tab does not have a custom tab component
|
||||
* (see {@link javax.swing.JTabbedPane#setTabComponentAt(int, java.awt.Component)}).
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Integer}
|
||||
*/
|
||||
String TABBED_PANE_MAXIMUM_TAB_WIDTH = "JTabbedPane.maximumTabWidth";
|
||||
|
||||
/**
|
||||
* Specifies the height of a tab.
|
||||
* <p>
|
||||
|
||||
@@ -118,6 +118,8 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
* @uiDefault TabbedPane.focusColor Color
|
||||
* @uiDefault TabbedPane.tabSeparatorColor Color optional; defaults to TabbedPane.contentAreaColor
|
||||
* @uiDefault TabbedPane.contentAreaColor Color
|
||||
* @uiDefault TabbedPane.minimumTabWidth int optional
|
||||
* @uiDefault TabbedPane.maximumTabWidth int optional
|
||||
* @uiDefault TabbedPane.tabHeight int
|
||||
* @uiDefault TabbedPane.tabSelectionHeight int
|
||||
* @uiDefault TabbedPane.contentSeparatorHeight int
|
||||
@@ -153,12 +155,15 @@ public class FlatTabbedPaneUI
|
||||
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;
|
||||
|
||||
private String hiddenTabsNavigationStr;
|
||||
protected Icon closeIcon;
|
||||
@@ -217,12 +222,15 @@ public class FlatTabbedPaneUI
|
||||
contentAreaColor = UIManager.getColor( "TabbedPane.contentAreaColor" );
|
||||
|
||||
textIconGapUnscaled = UIManager.getInt( "TabbedPane.textIconGap" );
|
||||
minimumTabWidth = UIManager.getInt( "TabbedPane.minimumTabWidth" );
|
||||
maximumTabWidth = UIManager.getInt( "TabbedPane.maximumTabWidth" );
|
||||
tabHeight = UIManager.getInt( "TabbedPane.tabHeight" );
|
||||
tabSelectionHeight = UIManager.getInt( "TabbedPane.tabSelectionHeight" );
|
||||
contentSeparatorHeight = UIManager.getInt( "TabbedPane.contentSeparatorHeight" );
|
||||
showTabSeparators = UIManager.getBoolean( "TabbedPane.showTabSeparators" );
|
||||
tabSeparatorsFullHeight = UIManager.getBoolean( "TabbedPane.tabSeparatorsFullHeight" );
|
||||
hasFullBorder = UIManager.getBoolean( "TabbedPane.hasFullBorder" );
|
||||
tabsOpaque = UIManager.getBoolean( "TabbedPane.tabsOpaque" );
|
||||
hiddenTabsNavigationStr = UIManager.getString( "TabbedPane.hiddenTabsNavigation" );
|
||||
closeIcon = UIManager.getIcon( "TabbedPane.closeIcon" );
|
||||
|
||||
@@ -518,8 +526,19 @@ public class FlatTabbedPaneUI
|
||||
textIconGap = scale( textIconGapUnscaled );
|
||||
|
||||
int tabWidth = super.calculateTabWidth( tabPlacement, tabIndex, metrics ) - 3 /* was added by superclass */;
|
||||
|
||||
// make tab wider if closable
|
||||
if( isTabClosable( tabIndex ) )
|
||||
tabWidth += closeIcon.getIconWidth();
|
||||
|
||||
// apply minimum and maximum tab width
|
||||
int min = getTabClientPropertyInt( tabIndex, TABBED_PANE_MINIMUM_TAB_WIDTH, minimumTabWidth );
|
||||
int max = getTabClientPropertyInt( tabIndex, TABBED_PANE_MAXIMUM_TAB_WIDTH, maximumTabWidth );
|
||||
if( min > 0 )
|
||||
tabWidth = Math.max( tabWidth, scale( min ) );
|
||||
if( max > 0 && tabPane.getTabComponentAt( tabIndex ) == null )
|
||||
tabWidth = Math.min( tabWidth, scale( max ) );
|
||||
|
||||
return tabWidth;
|
||||
}
|
||||
|
||||
@@ -649,6 +668,46 @@ public class FlatTabbedPaneUI
|
||||
paintTabArea( g, tabPlacement, selectedIndex );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintTab( Graphics g, int tabPlacement, Rectangle[] rects,
|
||||
int tabIndex, Rectangle iconRect, Rectangle textRect )
|
||||
{
|
||||
Rectangle tabRect = rects[tabIndex];
|
||||
boolean isSelected = (tabIndex == tabPane.getSelectedIndex());
|
||||
|
||||
// paint background
|
||||
if( tabsOpaque || tabPane.isOpaque() )
|
||||
paintTabBackground( g, tabPlacement, tabIndex, tabRect.x, tabRect.y, tabRect.width, tabRect.height, isSelected );
|
||||
|
||||
// paint border
|
||||
paintTabBorder( g, tabPlacement, tabIndex, tabRect.x, tabRect.y, tabRect.width, tabRect.height, isSelected );
|
||||
|
||||
if( tabPane.getTabComponentAt( tabIndex ) != null )
|
||||
return;
|
||||
|
||||
// layout title and icon
|
||||
String title = tabPane.getTitleAt( tabIndex );
|
||||
Icon icon = getIconForTab( tabIndex );
|
||||
Font font = tabPane.getFont();
|
||||
FontMetrics metrics = tabPane.getFontMetrics( font );
|
||||
String clippedTitle = layoutAndClipLabel( tabPlacement, metrics, tabIndex, title, icon, tabRect, iconRect, textRect, isSelected );
|
||||
|
||||
// special title clipping for scroll layout where title off last visible tab on right side may be truncated
|
||||
if( tabViewport != null && (tabPlacement == TOP || tabPlacement == BOTTOM) ) {
|
||||
Rectangle viewRect = tabViewport.getViewRect();
|
||||
viewRect.width -= 4; // subtract width of cropped edge
|
||||
if( !viewRect.contains( textRect ) ) {
|
||||
Rectangle r = viewRect.intersection( textRect );
|
||||
if( r.x > viewRect.x )
|
||||
clippedTitle = JavaCompatibility.getClippedString( null, metrics, title, r.width );
|
||||
}
|
||||
}
|
||||
|
||||
// paint title and icon
|
||||
paintText( g, tabPlacement, font, metrics, tabIndex, clippedTitle, textRect, isSelected );
|
||||
paintIcon( g, tabPlacement, tabIndex, icon, iconRect, isSelected );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintText( Graphics g, int tabPlacement, Font font, FontMetrics metrics,
|
||||
int tabIndex, String title, Rectangle textRect, boolean isSelected )
|
||||
@@ -662,20 +721,6 @@ public class FlatTabbedPaneUI
|
||||
return;
|
||||
}
|
||||
|
||||
// clip title if our layout manager is used
|
||||
// (normally this is done by invoker, but fails in this case)
|
||||
if( tabViewport != null &&
|
||||
(tabPlacement == TOP || tabPlacement == BOTTOM) )
|
||||
{
|
||||
Rectangle viewRect = tabViewport.getViewRect();
|
||||
viewRect.width -= 4; // subtract width of cropped edge
|
||||
if( !viewRect.contains( textRect ) ) {
|
||||
Rectangle r = viewRect.intersection( textRect );
|
||||
if( r.x > viewRect.x )
|
||||
title = JavaCompatibility.getClippedString( null, metrics, title, r.width );
|
||||
}
|
||||
}
|
||||
|
||||
// plain text
|
||||
Color color;
|
||||
if( tabPane.isEnabled() && tabPane.isEnabledAt( tabIndex ) ) {
|
||||
@@ -898,14 +943,37 @@ public class FlatTabbedPaneUI
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void layoutLabel( int tabPlacement, FontMetrics metrics, int tabIndex, String title, Icon icon,
|
||||
Rectangle tabRect, Rectangle iconRect, Rectangle textRect, boolean isSelected )
|
||||
protected String layoutAndClipLabel( int tabPlacement, FontMetrics metrics, int tabIndex,
|
||||
String title, Icon icon, Rectangle tabRect, Rectangle iconRect, Rectangle textRect, boolean isSelected )
|
||||
{
|
||||
// update textIconGap before used in super class
|
||||
textIconGap = scale( textIconGapUnscaled );
|
||||
// remove tab insets and space for close button from the tab rectangle
|
||||
// to get correctly clipped title
|
||||
tabRect = FlatUIUtils.subtractInsets( tabRect, getTabInsets( tabPlacement, tabIndex ) );
|
||||
if( isTabClosable( tabIndex ) ) {
|
||||
tabRect.width -= closeIcon.getIconWidth();
|
||||
if( !isLeftToRight() )
|
||||
tabRect.x += closeIcon.getIconWidth();
|
||||
}
|
||||
|
||||
super.layoutLabel( tabPlacement, metrics, tabIndex, title, icon, tabRect, iconRect, textRect, isSelected );
|
||||
// reset rectangles
|
||||
textRect.setBounds( 0, 0, 0, 0 );
|
||||
iconRect.setBounds( 0, 0, 0, 0 );
|
||||
|
||||
// temporary set "html" client property on tabbed pane, which is used by SwingUtilities.layoutCompoundLabel()
|
||||
View view = getTextViewForTab( tabIndex );
|
||||
if( view != null )
|
||||
tabPane.putClientProperty( "html", view );
|
||||
|
||||
// layout label
|
||||
String clippedTitle = SwingUtilities.layoutCompoundLabel( tabPane, metrics, title, icon,
|
||||
SwingUtilities.CENTER, SwingUtilities.CENTER,
|
||||
SwingUtilities.CENTER, SwingUtilities.TRAILING,
|
||||
tabRect, iconRect, textRect, scale( textIconGapUnscaled ) );
|
||||
|
||||
// remove temporary client property
|
||||
tabPane.putClientProperty( "html", null );
|
||||
|
||||
return clippedTitle;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -993,6 +1061,11 @@ public class FlatTabbedPaneUI
|
||||
return tabPane.getClientProperty( key );
|
||||
}
|
||||
|
||||
protected int getTabClientPropertyInt( int tabIndex, String key, int defaultValue ) {
|
||||
Object value = getTabClientProperty( tabIndex, key );
|
||||
return (value instanceof Integer) ? (int) value : defaultValue;
|
||||
}
|
||||
|
||||
protected void ensureCurrentLayout() {
|
||||
// since super.ensureCurrentLayout() is private,
|
||||
// use super.getTabRunCount() as workaround
|
||||
@@ -1718,6 +1791,8 @@ public class FlatTabbedPaneUI
|
||||
case TABBED_PANE_SHOW_TAB_SEPARATORS:
|
||||
case TABBED_PANE_SHOW_CONTENT_SEPARATOR:
|
||||
case TABBED_PANE_HAS_FULL_BORDER:
|
||||
case TABBED_PANE_MINIMUM_TAB_WIDTH:
|
||||
case TABBED_PANE_MAXIMUM_TAB_WIDTH:
|
||||
case TABBED_PANE_TAB_HEIGHT:
|
||||
case TABBED_PANE_TAB_INSETS:
|
||||
case TABBED_PANE_HIDDEN_TABS_NAVIGATION:
|
||||
@@ -1757,6 +1832,8 @@ public class FlatTabbedPaneUI
|
||||
|
||||
protected void contentPropertyChange( PropertyChangeEvent e ) {
|
||||
switch( e.getPropertyName() ) {
|
||||
case TABBED_PANE_MINIMUM_TAB_WIDTH:
|
||||
case TABBED_PANE_MAXIMUM_TAB_WIDTH:
|
||||
case TABBED_PANE_TAB_INSETS:
|
||||
case TABBED_PANE_TAB_CLOSABLE:
|
||||
tabPane.revalidate();
|
||||
|
||||
@@ -323,6 +323,16 @@ public class FlatContainerTest
|
||||
}
|
||||
}
|
||||
|
||||
private void minimumTabWidthChanged() {
|
||||
Integer minimumTabWidth = minimumTabWidthCheckBox.isSelected() ? 100 : null;
|
||||
putTabbedPanesClientProperty( TABBED_PANE_MINIMUM_TAB_WIDTH, minimumTabWidth );
|
||||
}
|
||||
|
||||
private void maximumTabWidthChanged() {
|
||||
Integer maximumTabWidth = maximumTabWidthCheckBox.isSelected() ? 60 : null;
|
||||
putTabbedPanesClientProperty( TABBED_PANE_MAXIMUM_TAB_WIDTH, maximumTabWidth );
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
|
||||
JPanel panel9 = new JPanel();
|
||||
@@ -365,6 +375,8 @@ public class FlatContainerTest
|
||||
trailingComponentCheckBox = new JCheckBox();
|
||||
showTabSeparatorsCheckBox = new JCheckBox();
|
||||
secondTabWiderCheckBox = new JCheckBox();
|
||||
minimumTabWidthCheckBox = new JCheckBox();
|
||||
maximumTabWidthCheckBox = new JCheckBox();
|
||||
CellConstraints cc = new CellConstraints();
|
||||
|
||||
//======== this ========
|
||||
@@ -480,6 +492,8 @@ public class FlatContainerTest
|
||||
"[]" +
|
||||
"[]para" +
|
||||
"[]" +
|
||||
"[]para" +
|
||||
"[]" +
|
||||
"[]"));
|
||||
|
||||
//---- tabScrollCheckBox ----
|
||||
@@ -605,6 +619,16 @@ public class FlatContainerTest
|
||||
secondTabWiderCheckBox.setText("Second Tab insets wider (4,20,4,20)");
|
||||
secondTabWiderCheckBox.addActionListener(e -> secondTabWiderChanged());
|
||||
tabbedPaneControlPanel.add(secondTabWiderCheckBox, "cell 2 6");
|
||||
|
||||
//---- minimumTabWidthCheckBox ----
|
||||
minimumTabWidthCheckBox.setText("Minimum tab width (100)");
|
||||
minimumTabWidthCheckBox.addActionListener(e -> minimumTabWidthChanged());
|
||||
tabbedPaneControlPanel.add(minimumTabWidthCheckBox, "cell 2 7");
|
||||
|
||||
//---- maximumTabWidthCheckBox ----
|
||||
maximumTabWidthCheckBox.setText("Maximum tab width (60)");
|
||||
maximumTabWidthCheckBox.addActionListener(e -> maximumTabWidthChanged());
|
||||
tabbedPaneControlPanel.add(maximumTabWidthCheckBox, "cell 2 8");
|
||||
}
|
||||
panel9.add(tabbedPaneControlPanel, cc.xywh(1, 11, 3, 1));
|
||||
}
|
||||
@@ -637,6 +661,8 @@ public class FlatContainerTest
|
||||
private JCheckBox trailingComponentCheckBox;
|
||||
private JCheckBox showTabSeparatorsCheckBox;
|
||||
private JCheckBox secondTabWiderCheckBox;
|
||||
private JCheckBox minimumTabWidthCheckBox;
|
||||
private JCheckBox maximumTabWidthCheckBox;
|
||||
// JFormDesigner - End of variables declaration //GEN-END:variables
|
||||
|
||||
//---- class Tab1Panel ----------------------------------------------------
|
||||
|
||||
@@ -132,7 +132,7 @@ new FormModel {
|
||||
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestFrame$NoRightToLeftPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||
"$layoutConstraints": "insets 0,hidemode 3"
|
||||
"$columnConstraints": "[][fill][]"
|
||||
"$rowConstraints": "[center][][]para[][]para[][]"
|
||||
"$rowConstraints": "[center][][]para[][]para[][]para[][]"
|
||||
} ) {
|
||||
name: "tabbedPaneControlPanel"
|
||||
"opaque": false
|
||||
@@ -381,6 +381,26 @@ new FormModel {
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 2 6"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||
name: "minimumTabWidthCheckBox"
|
||||
"text": "Minimum tab width (100)"
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": false
|
||||
}
|
||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "minimumTabWidthChanged", false ) )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 2 7"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||
name: "maximumTabWidthCheckBox"
|
||||
"text": "Maximum tab width (60)"
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": false
|
||||
}
|
||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "maximumTabWidthChanged", false ) )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 2 8"
|
||||
} )
|
||||
}, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) {
|
||||
"gridY": 11
|
||||
"gridWidth": 3
|
||||
|
||||
Reference in New Issue
Block a user