TabbedPane: reworked painting in scroll-tab-layout, so that the separator line now spans the whole width and is no longer interrupted by the scroll buttons

This commit is contained in:
Karl Tauber
2019-11-09 10:58:31 +01:00
parent d5944779e8
commit 7f50a30b29
2 changed files with 71 additions and 54 deletions

View File

@@ -5,6 +5,8 @@ FlatLaf Change Log
- TextField and TextArea: Do not apply minimum width if `columns` property is - TextField and TextArea: Do not apply minimum width if `columns` property is
greater than zero. greater than zero.
- TabbedPane: In scroll-tab-layout, the separator line now spans the whole width
and is no longer interrupted by the scroll buttons.
## 0.17 ## 0.17

View File

@@ -19,11 +19,14 @@ package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale; import static com.formdev.flatlaf.util.UIScale.scale;
import static com.formdev.flatlaf.FlatClientProperties.*; import static com.formdev.flatlaf.FlatClientProperties.*;
import java.awt.Color; import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Font; import java.awt.Font;
import java.awt.FontMetrics; import java.awt.FontMetrics;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Insets; import java.awt.Insets;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Shape;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import javax.swing.JButton; import javax.swing.JButton;
@@ -177,19 +180,16 @@ public class FlatTabbedPaneUI
@Override @Override
protected int calculateTabWidth( int tabPlacement, int tabIndex, FontMetrics metrics ) { protected int calculateTabWidth( int tabPlacement, int tabIndex, FontMetrics metrics ) {
return super.calculateTabWidth( tabPlacement, tabIndex, metrics ) - 3 // was added by superclass return super.calculateTabWidth( tabPlacement, tabIndex, metrics ) - 3 /* was added by superclass */;
+ (!isTopOrBottom( tabPlacement ) && isScrollTabLayout() ? contentSeparatorHeight : 0);
} }
@Override @Override
protected int calculateTabHeight( int tabPlacement, int tabIndex, int fontHeight ) { protected int calculateTabHeight( int tabPlacement, int tabIndex, int fontHeight ) {
return Math.max( tabHeight, super.calculateTabHeight( tabPlacement, tabIndex, fontHeight ) - 2 /* was added by superclass */ ) return Math.max( tabHeight, super.calculateTabHeight( tabPlacement, tabIndex, fontHeight ) - 2 /* was added by superclass */ );
+ (isTopOrBottom( tabPlacement ) && isScrollTabLayout() ? contentSeparatorHeight : 0);
} }
/** /**
* The content border insets are used to create a separator between tabs and content. * The content border insets are used to create a separator between tabs and content.
* Except in scroll tab policy, where the separator is painted in paintTabArea().
* If client property JTabbedPane.hasFullBorder is true, then the content border insets * If client property JTabbedPane.hasFullBorder is true, then the content border insets
* are also used for the border. * are also used for the border.
*/ */
@@ -199,48 +199,18 @@ public class FlatTabbedPaneUI
int sh = contentSeparatorHeight; int sh = contentSeparatorHeight;
Insets insets = hasFullBorder ? new Insets( sh, sh, sh, sh ) : new Insets( sh, 0, 0, 0 ); Insets insets = hasFullBorder ? new Insets( sh, sh, sh, sh ) : new Insets( sh, 0, 0, 0 );
if( isScrollTabLayout() )
insets.top = 0;
rotateInsets( insets, contentBorderInsets, tabPlacement ); rotateInsets( insets, contentBorderInsets, tabPlacement );
return contentBorderInsets; return contentBorderInsets;
} }
@Override @Override
protected int getTabLabelShiftX( int tabPlacement, int tabIndex, boolean isSelected ) { protected int getTabLabelShiftX( int tabPlacement, int tabIndex, boolean isSelected ) {
if( isScrollTabLayout() && !isTopOrBottom( tabPlacement ) ) { return 0;
float shift = contentSeparatorHeight / 2f;
return Math.round( tabPlacement == LEFT ? -shift : shift );
} else
return 0;
} }
@Override @Override
protected int getTabLabelShiftY( int tabPlacement, int tabIndex, boolean isSelected ) { protected int getTabLabelShiftY( int tabPlacement, int tabIndex, boolean isSelected ) {
if( isScrollTabLayout() && isTopOrBottom( tabPlacement ) ) { return 0;
float shift = contentSeparatorHeight / 2f;
return Math.round( tabPlacement == TOP ? -shift : shift );
} else
return 0;
}
@Override
protected void paintTabArea( Graphics g, int tabPlacement, int selectedIndex ) {
if( isScrollTabLayout() ) {
// paint separator between tabs and content
Rectangle bounds = g.getClipBounds();
g.setColor( contentAreaColor );
if( tabPlacement == TOP || tabPlacement == BOTTOM ) {
int y = (tabPlacement == TOP) ? bounds.y + bounds.height - contentSeparatorHeight : bounds.y;
g.fillRect( bounds.x, y, bounds.x + bounds.width, contentSeparatorHeight );
} else {
int x = (tabPlacement == LEFT) ? bounds.x + bounds.width - contentSeparatorHeight : bounds.x;
g.fillRect( x, bounds.y, contentSeparatorHeight, bounds.y + bounds.height );
}
}
super.paintTabArea( g, tabPlacement, selectedIndex );
} }
@Override @Override
@@ -276,19 +246,6 @@ public class FlatTabbedPaneUI
protected void paintTabBackground( Graphics g, int tabPlacement, int tabIndex, protected void paintTabBackground( Graphics g, int tabPlacement, int tabIndex,
int x, int y, int w, int h, boolean isSelected ) int x, int y, int w, int h, boolean isSelected )
{ {
if( isScrollTabLayout() ) {
// make tab bounds smaller for separator between tabs and content
if( tabPlacement == TOP || tabPlacement == BOTTOM ) {
if( tabPlacement == BOTTOM )
y += contentSeparatorHeight;
h -= contentSeparatorHeight;
} else {
if( tabPlacement == RIGHT )
x += contentSeparatorHeight;
w -= contentSeparatorHeight;
}
}
// paint tab background // paint tab background
boolean enabled = tabPane.isEnabled(); boolean enabled = tabPane.isEnabled();
g.setColor( enabled && tabPane.isEnabledAt( tabIndex ) && getRolloverTab() == tabIndex g.setColor( enabled && tabPane.isEnabledAt( tabIndex ) && getRolloverTab() == tabIndex
@@ -303,8 +260,37 @@ public class FlatTabbedPaneUI
protected void paintTabBorder( Graphics g, int tabPlacement, int tabIndex, protected void paintTabBorder( Graphics g, int tabPlacement, int tabIndex,
int x, int y, int w, int h, boolean isSelected ) int x, int y, int w, int h, boolean isSelected )
{ {
if( !isSelected ) if( isSelected )
return; paintTabSelection( g, tabPlacement, x, y, w, h );
}
protected void paintTabSelection( Graphics g, int tabPlacement, int x, int y, int w, int h ) {
// increase clip bounds in scroll-tab-layout to paint over the separator line
Rectangle clipBounds = isScrollTabLayout() ? g.getClipBounds() : null;
if( clipBounds != null ) {
Rectangle newClipBounds = new Rectangle( clipBounds );
switch( tabPlacement ) {
case TOP:
default:
newClipBounds.height += contentSeparatorHeight;
break;
case BOTTOM:
newClipBounds.y -= contentSeparatorHeight;
newClipBounds.height += contentSeparatorHeight;
break;
case LEFT:
newClipBounds.width += contentSeparatorHeight;
break;
case RIGHT:
newClipBounds.x -= contentSeparatorHeight;
newClipBounds.width += contentSeparatorHeight;
break;
}
g.setClip( newClipBounds );
}
g.setColor( tabPane.isEnabled() ? underlineColor : disabledUnderlineColor ); g.setColor( tabPane.isEnabled() ? underlineColor : disabledUnderlineColor );
@@ -331,6 +317,9 @@ public class FlatTabbedPaneUI
g.fillRect( x - contentInsets.right, y, tabSelectionHeight, h ); g.fillRect( x - contentInsets.right, y, tabSelectionHeight, h );
break; break;
} }
if( clipBounds != null )
g.setClip( clipBounds );
} }
/** /**
@@ -338,6 +327,7 @@ public class FlatTabbedPaneUI
* - content pane is always opaque * - content pane is always opaque
* - not using UIManager.getColor("TabbedPane.contentAreaColor") to be GUI builder friendly * - not using UIManager.getColor("TabbedPane.contentAreaColor") to be GUI builder friendly
* - not invoking paintContentBorder*Edge() methods * - not invoking paintContentBorder*Edge() methods
* - repaint selection
*/ */
@Override @Override
protected void paintContentBorder( Graphics g, int tabPlacement, int selectedIndex ) { protected void paintContentBorder( Graphics g, int tabPlacement, int selectedIndex ) {
@@ -381,6 +371,21 @@ public class FlatTabbedPaneUI
// paint content area // paint content area
g.setColor( contentAreaColor ); g.setColor( contentAreaColor );
g.fillRect( x, y, w, h ); g.fillRect( x, y, w, h );
// repaint selection in scroll-tab-layout because it may be painted before
// the content border was painted (from BasicTabbedPaneUI$ScrollableTabPanel)
if( isScrollTabLayout() && selectedIndex >= 0 ) {
Component scrollableTabViewport = findComponentByClassName( tabPane,
BasicTabbedPaneUI.class.getName() + "$ScrollableTabViewport" );
if( scrollableTabViewport != null ) {
Rectangle tabRect = getTabBounds( tabPane, selectedIndex );
Shape oldClip = g.getClip();
g.setClip( scrollableTabViewport.getBounds() );
paintTabSelection( g, tabPlacement, tabRect.x, tabRect.y, tabRect.width, tabRect.height );
g.setClip( oldClip );
}
}
} }
@Override @Override
@@ -393,7 +398,17 @@ public class FlatTabbedPaneUI
return tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT; return tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT;
} }
private boolean isTopOrBottom( int tabPlacement ) { private Component findComponentByClassName( Container c, String className ) {
return tabPlacement == TOP || tabPlacement == BOTTOM; for( Component child : c.getComponents() ) {
if( className.equals( child.getClass().getName() ) )
return child;
if( child instanceof Container ) {
Component c2 = findComponentByClassName( (Container) child, className );
if( c2 != null )
return c2;
}
}
return null;
} }
} }