mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-07 22:40:53 +03:00
TabbedPane: support equal and compact tab width modes
This commit is contained in:
@@ -21,9 +21,13 @@ FlatLaf Change Log
|
||||
- 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)
|
||||
(PR #199)
|
||||
- TabbedPane: Support alignment of tab area. (set client property
|
||||
`JTabbedPane.tabAreaAlignment` to `"leading"`, `"trailing"`, `"center"` or
|
||||
`"fill"`)
|
||||
`"fill"`) (PR #199)
|
||||
- TabbedPane: Support equal and compact tab width modes. (set client property
|
||||
`JTabbedPane.tabWidthMode` to `"preferred"`, `"equal"` or `"compact"`) (PR
|
||||
#199)
|
||||
- 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
|
||||
|
||||
@@ -410,6 +410,38 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String TABBED_PANE_TAB_AREA_ALIGN_FILL = "fill";
|
||||
|
||||
/**
|
||||
* Specifies how the tabs should be sized.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.String}
|
||||
* <strong>Allowed Values</strong> {@link #TABBED_PANE_TAB_WIDTH_MODE_PREFERRED} (default),
|
||||
* {@link #TABBED_PANE_TAB_WIDTH_MODE_EQUAL} or {@link #TABBED_PANE_TAB_WIDTH_MODE_COMPACT}
|
||||
*/
|
||||
String TABBED_PANE_TAB_WIDTH_MODE = "JTabbedPane.tabWidthMode";
|
||||
|
||||
/**
|
||||
* Tab width is adjusted to tab icon and title.
|
||||
*
|
||||
* @see #TABBED_PANE_TAB_WIDTH_MODE
|
||||
*/
|
||||
String TABBED_PANE_TAB_WIDTH_MODE_PREFERRED = "preferred";
|
||||
|
||||
/**
|
||||
* All tabs in a tabbed pane has same width.
|
||||
*
|
||||
* @see #TABBED_PANE_TAB_WIDTH_MODE
|
||||
*/
|
||||
String TABBED_PANE_TAB_WIDTH_MODE_EQUAL = "equal";
|
||||
|
||||
/**
|
||||
* Unselected tabs are smaller because they show only the tab icon, but no tab title.
|
||||
* Selected tabs show both.
|
||||
*
|
||||
* @see #TABBED_PANE_TAB_WIDTH_MODE
|
||||
*/
|
||||
String TABBED_PANE_TAB_WIDTH_MODE_COMPACT = "compact";
|
||||
|
||||
/**
|
||||
* Specifies a component that will be placed at the leading edge of the tabs area.
|
||||
* <p>
|
||||
|
||||
@@ -128,6 +128,7 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
* @uiDefault TabbedPane.hasFullBorder boolean
|
||||
* @uiDefault TabbedPane.hiddenTabsNavigation String moreTabsButton (default) or arrowButtons
|
||||
* @uiDefault TabbedPane.tabAreaAlignment String leading (default), center, trailing or fill
|
||||
* @uiDefault TabbedPane.tabWidthMode String preferred (default), equal or compact
|
||||
* @uiDefault ScrollPane.smoothScrolling boolean
|
||||
* @uiDefault TabbedPane.closeIcon Icon
|
||||
*
|
||||
@@ -148,6 +149,10 @@ public class FlatTabbedPaneUI
|
||||
protected static final int ALIGN_CENTER = 2;
|
||||
protected static final int ALIGN_FILL = 3;
|
||||
|
||||
protected static final int WIDTH_MODE_PREFERRED = 0;
|
||||
protected static final int WIDTH_MODE_EQUAL = 1;
|
||||
protected static final int WIDTH_MODE_COMPACT = 2;
|
||||
|
||||
private static Set<KeyStroke> focusForwardTraversalKeys;
|
||||
private static Set<KeyStroke> focusBackwardTraversalKeys;
|
||||
|
||||
@@ -174,6 +179,7 @@ public class FlatTabbedPaneUI
|
||||
|
||||
private String hiddenTabsNavigationStr;
|
||||
private String tabAreaAlignmentStr;
|
||||
private String tabWidthModeStr;
|
||||
protected Icon closeIcon;
|
||||
|
||||
protected String moreTabsButtonToolTipText;
|
||||
@@ -241,6 +247,7 @@ public class FlatTabbedPaneUI
|
||||
tabsOpaque = UIManager.getBoolean( "TabbedPane.tabsOpaque" );
|
||||
hiddenTabsNavigationStr = UIManager.getString( "TabbedPane.hiddenTabsNavigation" );
|
||||
tabAreaAlignmentStr = UIManager.getString( "TabbedPane.tabAreaAlignment" );
|
||||
tabWidthModeStr = UIManager.getString( "TabbedPane.tabWidthMode" );
|
||||
closeIcon = UIManager.getIcon( "TabbedPane.closeIcon" );
|
||||
|
||||
Locale l = tabPane.getLocale();
|
||||
@@ -529,12 +536,35 @@ public class FlatTabbedPaneUI
|
||||
tabPane.repaint( r );
|
||||
}
|
||||
|
||||
private boolean inCalculateEqual;
|
||||
|
||||
@Override
|
||||
protected int calculateTabWidth( int tabPlacement, int tabIndex, FontMetrics metrics ) {
|
||||
int tabWidthMode = getTabWidthMode();
|
||||
if( tabWidthMode == WIDTH_MODE_EQUAL && isHorizontalTabPlacement() && !inCalculateEqual ) {
|
||||
inCalculateEqual = true;
|
||||
try {
|
||||
return calculateMaxTabWidth( tabPlacement );
|
||||
} finally {
|
||||
inCalculateEqual = false;
|
||||
}
|
||||
}
|
||||
|
||||
// update textIconGap before used in super class
|
||||
textIconGap = scale( textIconGapUnscaled );
|
||||
|
||||
int tabWidth = super.calculateTabWidth( tabPlacement, tabIndex, metrics ) - 3 /* was added by superclass */;
|
||||
int tabWidth;
|
||||
Icon icon;
|
||||
if( tabWidthMode == WIDTH_MODE_COMPACT &&
|
||||
tabIndex != tabPane.getSelectedIndex() &&
|
||||
isHorizontalTabPlacement() &&
|
||||
tabPane.getTabComponentAt( tabIndex ) == null &&
|
||||
(icon = getIconForTab( tabIndex )) != null )
|
||||
{
|
||||
Insets tabInsets = getTabInsets( tabPlacement, tabIndex );
|
||||
tabWidth = icon.getIconWidth() + tabInsets.left + tabInsets.right;
|
||||
} else
|
||||
tabWidth = super.calculateTabWidth( tabPlacement, tabIndex, metrics ) - 3 /* was added by superclass */;
|
||||
|
||||
// make tab wider if closable
|
||||
if( isTabClosable( tabIndex ) )
|
||||
@@ -663,14 +693,26 @@ public class FlatTabbedPaneUI
|
||||
int tabIndex, Rectangle iconRect, Rectangle textRect )
|
||||
{
|
||||
Rectangle tabRect = rects[tabIndex];
|
||||
int x = tabRect.x;
|
||||
int y = tabRect.y;
|
||||
int w = tabRect.width;
|
||||
int h = tabRect.height;
|
||||
boolean isSelected = (tabIndex == tabPane.getSelectedIndex());
|
||||
|
||||
// paint background
|
||||
if( tabsOpaque || tabPane.isOpaque() )
|
||||
paintTabBackground( g, tabPlacement, tabIndex, tabRect.x, tabRect.y, tabRect.width, tabRect.height, isSelected );
|
||||
paintTabBackground( g, tabPlacement, tabIndex, x, y, w, h, isSelected );
|
||||
|
||||
// paint border
|
||||
paintTabBorder( g, tabPlacement, tabIndex, tabRect.x, tabRect.y, tabRect.width, tabRect.height, isSelected );
|
||||
paintTabBorder( g, tabPlacement, tabIndex, x, y, w, h, isSelected );
|
||||
|
||||
// paint tab close button
|
||||
if( isTabClosable( tabIndex ) )
|
||||
paintTabCloseButton( g, tabIndex, x, y, w, h );
|
||||
|
||||
// paint selection indicator
|
||||
if( isSelected )
|
||||
paintTabSelection( g, tabPlacement, x, y, w, h );
|
||||
|
||||
if( tabPane.getTabComponentAt( tabIndex ) != null )
|
||||
return;
|
||||
@@ -680,9 +722,12 @@ public class FlatTabbedPaneUI
|
||||
Icon icon = getIconForTab( tabIndex );
|
||||
Font font = tabPane.getFont();
|
||||
FontMetrics metrics = tabPane.getFontMetrics( font );
|
||||
boolean isCompact = (icon != null && !isSelected && getTabWidthMode() == WIDTH_MODE_COMPACT && isHorizontalTabPlacement());
|
||||
if( isCompact )
|
||||
title = null;
|
||||
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
|
||||
// special title clipping for scroll layout where title of 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
|
||||
@@ -694,7 +739,8 @@ public class FlatTabbedPaneUI
|
||||
}
|
||||
|
||||
// paint title and icon
|
||||
paintText( g, tabPlacement, font, metrics, tabIndex, clippedTitle, textRect, isSelected );
|
||||
if( !isCompact )
|
||||
paintText( g, tabPlacement, font, metrics, tabIndex, clippedTitle, textRect, isSelected );
|
||||
paintIcon( g, tabPlacement, tabIndex, icon, iconRect, isSelected );
|
||||
}
|
||||
|
||||
@@ -747,17 +793,10 @@ public class FlatTabbedPaneUI
|
||||
protected void paintTabBorder( Graphics g, int tabPlacement, int tabIndex,
|
||||
int x, int y, int w, int h, boolean isSelected )
|
||||
{
|
||||
// paint tab close button
|
||||
if( isTabClosable( tabIndex ) )
|
||||
paintTabCloseButton( g, tabIndex, x, y, w, h );
|
||||
|
||||
// paint tab separators
|
||||
if( clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_TAB_SEPARATORS, showTabSeparators ) &&
|
||||
!isLastInRun( tabIndex ) )
|
||||
paintTabSeparator( g, tabPlacement, x, y, w, h );
|
||||
|
||||
if( isSelected )
|
||||
paintTabSelection( g, tabPlacement, x, y, w, h );
|
||||
}
|
||||
|
||||
protected void paintTabCloseButton( Graphics g, int tabIndex, int x, int y, int w, int h ) {
|
||||
@@ -1104,6 +1143,13 @@ public class FlatTabbedPaneUI
|
||||
return parseTabAreaAlignment( str );
|
||||
}
|
||||
|
||||
protected int getTabWidthMode() {
|
||||
String str = (String) tabPane.getClientProperty( TABBED_PANE_TAB_WIDTH_MODE );
|
||||
if( str == null )
|
||||
str = tabWidthModeStr;
|
||||
return parseTabWidthMode( str );
|
||||
}
|
||||
|
||||
protected static int parseHiddenTabsNavigation( String str ) {
|
||||
if( str == null )
|
||||
return MORE_TABS_BUTTON;
|
||||
@@ -1128,6 +1174,18 @@ public class FlatTabbedPaneUI
|
||||
}
|
||||
}
|
||||
|
||||
protected static int parseTabWidthMode( String str ) {
|
||||
if( str == null )
|
||||
return WIDTH_MODE_PREFERRED;
|
||||
|
||||
switch( str ) {
|
||||
default:
|
||||
case TABBED_PANE_TAB_WIDTH_MODE_PREFERRED: return WIDTH_MODE_PREFERRED;
|
||||
case TABBED_PANE_TAB_WIDTH_MODE_EQUAL: return WIDTH_MODE_EQUAL;
|
||||
case TABBED_PANE_TAB_WIDTH_MODE_COMPACT: return WIDTH_MODE_COMPACT;
|
||||
}
|
||||
}
|
||||
|
||||
private void runWithOriginalLayoutManager( Runnable runnable ) {
|
||||
LayoutManager layout = tabPane.getLayout();
|
||||
if( layout instanceof FlatTabbedPaneScrollLayout ) {
|
||||
@@ -1881,6 +1939,7 @@ public class FlatTabbedPaneUI
|
||||
case TABBED_PANE_TAB_INSETS:
|
||||
case TABBED_PANE_HIDDEN_TABS_NAVIGATION:
|
||||
case TABBED_PANE_TAB_AREA_ALIGNMENT:
|
||||
case TABBED_PANE_TAB_WIDTH_MODE:
|
||||
case TABBED_PANE_TAB_CLOSABLE:
|
||||
tabPane.revalidate();
|
||||
tabPane.repaint();
|
||||
|
||||
@@ -172,10 +172,8 @@ public class FlatContainerTest
|
||||
? new ScaledImageIcon( new ImageIcon( getClass().getResource( "/com/formdev/flatlaf/testing/test" + iconSize + ".png" ) ) )
|
||||
: null;
|
||||
int tabCount = tabbedPane.getTabCount();
|
||||
if( tabCount > 0 )
|
||||
tabbedPane.setIconAt( 0, icon );
|
||||
if( tabCount > 1 )
|
||||
tabbedPane.setIconAt( 1, icon );
|
||||
for( int i = 0; i < tabCount; i++ )
|
||||
tabbedPane.setIconAt( i, icon );
|
||||
}
|
||||
|
||||
private void customBorderChanged() {
|
||||
@@ -252,6 +250,13 @@ public class FlatContainerTest
|
||||
putTabbedPanesClientProperty( TABBED_PANE_TAB_AREA_ALIGNMENT, value );
|
||||
}
|
||||
|
||||
private void tabWidthModeChanged() {
|
||||
String value = (String) tabWidthModeField.getSelectedItem();
|
||||
if( "default".equals( value ) )
|
||||
value = null;
|
||||
putTabbedPanesClientProperty( TABBED_PANE_TAB_WIDTH_MODE, value );
|
||||
}
|
||||
|
||||
private void tabBackForegroundChanged() {
|
||||
tabBackForegroundChanged( tabbedPane1 );
|
||||
tabBackForegroundChanged( tabbedPane2 );
|
||||
@@ -387,6 +392,8 @@ public class FlatContainerTest
|
||||
tabIconSizeSpinner = new JSpinner();
|
||||
JLabel tabAreaAlignmentLabel = new JLabel();
|
||||
tabAreaAlignmentField = new JComboBox<>();
|
||||
JLabel tabWidthModeLabel = new JLabel();
|
||||
tabWidthModeField = new JComboBox<>();
|
||||
tabsClosableCheckBox = new JCheckBox();
|
||||
customBorderCheckBox = new JCheckBox();
|
||||
tabAreaInsetsCheckBox = new JCheckBox();
|
||||
@@ -600,6 +607,20 @@ public class FlatContainerTest
|
||||
tabAreaAlignmentField.addActionListener(e -> tabAreaAlignmentChanged());
|
||||
tabbedPaneControlPanel.add(tabAreaAlignmentField, "cell 1 3");
|
||||
|
||||
//---- tabWidthModeLabel ----
|
||||
tabWidthModeLabel.setText("Tab width mode:");
|
||||
tabbedPaneControlPanel.add(tabWidthModeLabel, "cell 2 3");
|
||||
|
||||
//---- tabWidthModeField ----
|
||||
tabWidthModeField.setModel(new DefaultComboBoxModel<>(new String[] {
|
||||
"default",
|
||||
"preferred",
|
||||
"equal",
|
||||
"compact"
|
||||
}));
|
||||
tabWidthModeField.addActionListener(e -> tabWidthModeChanged());
|
||||
tabbedPaneControlPanel.add(tabWidthModeField, "cell 2 3");
|
||||
|
||||
//---- tabsClosableCheckBox ----
|
||||
tabsClosableCheckBox.setText("Tabs closable");
|
||||
tabsClosableCheckBox.addActionListener(e -> tabsClosableChanged());
|
||||
@@ -690,6 +711,7 @@ public class FlatContainerTest
|
||||
private JCheckBox tabIconsCheckBox;
|
||||
private JSpinner tabIconSizeSpinner;
|
||||
private JComboBox<String> tabAreaAlignmentField;
|
||||
private JComboBox<String> tabWidthModeField;
|
||||
private JCheckBox tabsClosableCheckBox;
|
||||
private JCheckBox customBorderCheckBox;
|
||||
private JCheckBox tabAreaInsetsCheckBox;
|
||||
|
||||
@@ -285,6 +285,29 @@ new FormModel {
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 3"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "tabWidthModeLabel"
|
||||
"text": "Tab width mode:"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 2 3"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JComboBox" ) {
|
||||
name: "tabWidthModeField"
|
||||
"model": new javax.swing.DefaultComboBoxModel {
|
||||
selectedItem: "default"
|
||||
addElement( "default" )
|
||||
addElement( "preferred" )
|
||||
addElement( "equal" )
|
||||
addElement( "compact" )
|
||||
}
|
||||
auxiliary() {
|
||||
"JavaCodeGenerator.variableLocal": false
|
||||
"JavaCodeGenerator.typeParameters": "String"
|
||||
}
|
||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabWidthModeChanged", false ) )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 2 3"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||
name: "tabsClosableCheckBox"
|
||||
"text": "Tabs closable"
|
||||
|
||||
Reference in New Issue
Block a user