mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-07 06:20:53 +03:00
TabbedPane: reviewed PR #343 (active tab border painting style)
- moved focus listener to class `Handler` - instead of repainting content border in `repaintContentBorder()`, increase size of repaint region in `repaintTab()` to include part of content border - simplified `paintContentBorder()` by using `getTabBounds()` and `Rectangle2D.intersect()` to compute gap rectangle - slightly make code smaller - minor formatting, renaming, ...
This commit is contained in:
@@ -49,7 +49,6 @@ import java.awt.event.MouseEvent;
|
|||||||
import java.awt.event.MouseListener;
|
import java.awt.event.MouseListener;
|
||||||
import java.awt.event.MouseMotionListener;
|
import java.awt.event.MouseMotionListener;
|
||||||
import java.awt.event.MouseWheelEvent;
|
import java.awt.event.MouseWheelEvent;
|
||||||
import java.awt.geom.Area;
|
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
@@ -236,8 +235,6 @@ public class FlatTabbedPaneUI
|
|||||||
private boolean rolloverTabClose;
|
private boolean rolloverTabClose;
|
||||||
private boolean pressedTabClose;
|
private boolean pressedTabClose;
|
||||||
|
|
||||||
private FocusListener contentBorderFocusListener;
|
|
||||||
|
|
||||||
private Object[] oldRenderingHints;
|
private Object[] oldRenderingHints;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
@@ -480,25 +477,6 @@ public class FlatTabbedPaneUI
|
|||||||
tabPane.addMouseMotionListener( wheelTabScroller );
|
tabPane.addMouseMotionListener( wheelTabScroller );
|
||||||
tabPane.addMouseListener( wheelTabScroller );
|
tabPane.addMouseListener( wheelTabScroller );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix for content border repainting issue (when activeTabBorder is on) //TODO: Find a better solution perhaps
|
|
||||||
/* Basically the content separator paints part of itself with the color of the active tab.
|
|
||||||
* When the active tab has focus, and focus is lost to a different component (outside the tabbed pane)
|
|
||||||
* the content separator does not get repainted, but the active tab background does. I don't know why.
|
|
||||||
*/
|
|
||||||
contentBorderFocusListener = new FocusListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void focusGained( FocusEvent e ) {
|
|
||||||
repaintContentBorder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void focusLost( FocusEvent e ) {
|
|
||||||
repaintContentBorder();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
tabPane.addFocusListener( contentBorderFocusListener );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -518,8 +496,6 @@ public class FlatTabbedPaneUI
|
|||||||
tabPane.removeMouseListener( wheelTabScroller );
|
tabPane.removeMouseListener( wheelTabScroller );
|
||||||
wheelTabScroller = null;
|
wheelTabScroller = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
tabPane.removeFocusListener( contentBorderFocusListener );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -567,6 +543,13 @@ public class FlatTabbedPaneUI
|
|||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected FocusListener createFocusListener() {
|
||||||
|
Handler handler = getHandler();
|
||||||
|
handler.focusDelegate = super.createFocusListener();
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected LayoutManager createLayoutManager() {
|
protected LayoutManager createLayoutManager() {
|
||||||
if( tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT )
|
if( tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT )
|
||||||
@@ -632,45 +615,30 @@ public class FlatTabbedPaneUI
|
|||||||
repaintTab( getRolloverTab() );
|
repaintTab( getRolloverTab() );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Repaints parts of the content border if necessary.
|
|
||||||
* @see com.formdev.flatlaf.FlatClientProperties#TABBED_PANE_ACTIVE_TAB_BORDER
|
|
||||||
*/
|
|
||||||
private void repaintContentBorder() {
|
|
||||||
if ( clientPropertyBoolean( tabPane, TABBED_PANE_ACTIVE_TAB_BORDER, activeTabBorder ) &&
|
|
||||||
clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, true ))
|
|
||||||
{
|
|
||||||
int csh = scale(contentSeparatorHeight);
|
|
||||||
Rectangle bounds = getContentBorderBounds(tabPane.getTabPlacement());
|
|
||||||
Rectangle r;
|
|
||||||
switch (tabPane.getTabPlacement()) {
|
|
||||||
default:
|
|
||||||
case TOP:
|
|
||||||
r = new Rectangle( bounds.x, bounds.y, bounds.width, csh);
|
|
||||||
break;
|
|
||||||
case BOTTOM:
|
|
||||||
r = new Rectangle( bounds.x, bounds.y + bounds.height - csh, bounds.width, csh);
|
|
||||||
break;
|
|
||||||
case LEFT:
|
|
||||||
r = new Rectangle( bounds.x, bounds.y, csh, bounds.height);
|
|
||||||
break;
|
|
||||||
case RIGHT:
|
|
||||||
r = new Rectangle( bounds.x + bounds.width - csh, bounds.y, csh, bounds.height);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
tabPane.repaint(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void repaintTab( int tabIndex ) {
|
private void repaintTab( int tabIndex ) {
|
||||||
if( tabIndex < 0 || tabIndex >= tabPane.getTabCount() )
|
if( tabIndex < 0 || tabIndex >= tabPane.getTabCount() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Rectangle r = getTabBounds( tabPane, tabIndex );
|
Rectangle r = getTabBounds( tabPane, tabIndex );
|
||||||
if( r != null )
|
if( r == null )
|
||||||
tabPane.repaint( r );
|
return;
|
||||||
|
|
||||||
repaintContentBorder();
|
// increase size of repaint region to include part of content border
|
||||||
|
if( contentSeparatorHeight > 0 &&
|
||||||
|
clientPropertyBoolean( tabPane, TABBED_PANE_ACTIVE_TAB_BORDER, activeTabBorder ) &&
|
||||||
|
clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, true ) )
|
||||||
|
{
|
||||||
|
int sh = scale( contentSeparatorHeight );
|
||||||
|
switch( tabPane.getTabPlacement() ) {
|
||||||
|
default:
|
||||||
|
case TOP: r.height += sh; break;
|
||||||
|
case BOTTOM: r.height += sh; r.y -= sh; break;
|
||||||
|
case LEFT: r.width += sh; break;
|
||||||
|
case RIGHT: r.width += sh; r.x -= sh; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tabPane.repaint( r );
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean inCalculateEqual;
|
private boolean inCalculateEqual;
|
||||||
@@ -887,7 +855,7 @@ public class FlatTabbedPaneUI
|
|||||||
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
oldRenderingHints = null;
|
oldRenderingHints = null;
|
||||||
}
|
}
|
||||||
static int cnt = 0;
|
|
||||||
@Override
|
@Override
|
||||||
public void paint( Graphics g, JComponent c ) {
|
public void paint( Graphics g, JComponent c ) {
|
||||||
if( hideTabArea() )
|
if( hideTabArea() )
|
||||||
@@ -904,6 +872,17 @@ public class FlatTabbedPaneUI
|
|||||||
paintTabArea( g, tabPlacement, selectedIndex );
|
paintTabArea( g, tabPlacement, selectedIndex );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintTabArea( Graphics g, int tabPlacement, int selectedIndex ) {
|
||||||
|
// need to set rendering hints here too because this method is also invoked
|
||||||
|
// from BasicTabbedPaneUI.ScrollableTabPanel.paintComponent()
|
||||||
|
Object[] oldHints = FlatUIUtils.setRenderingHints( g );
|
||||||
|
|
||||||
|
super.paintTabArea( g, tabPlacement, selectedIndex );
|
||||||
|
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldHints );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintTab( Graphics g, int tabPlacement, Rectangle[] rects,
|
protected void paintTab( Graphics g, int tabPlacement, Rectangle[] rects,
|
||||||
int tabIndex, Rectangle iconRect, Rectangle textRect )
|
int tabIndex, Rectangle iconRect, Rectangle textRect )
|
||||||
@@ -996,21 +975,20 @@ public class FlatTabbedPaneUI
|
|||||||
int x, int y, int w, int h, boolean isSelected )
|
int x, int y, int w, int h, boolean isSelected )
|
||||||
{
|
{
|
||||||
// paint tab background
|
// paint tab background
|
||||||
Color background = determineTabBackgroundColor(tabPlacement, tabIndex, isSelected);
|
Color background = getTabBackground( tabPlacement, tabIndex, isSelected );
|
||||||
g.setColor( FlatUIUtils.deriveColor( background, tabPane.getBackground() ) );
|
g.setColor( FlatUIUtils.deriveColor( background, tabPane.getBackground() ) );
|
||||||
g.fillRect( x, y, w, h );
|
g.fillRect( x, y, w, h );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Color determineTabBackgroundColor(int tabPlacement, int tabIndex, boolean isSelected) {
|
protected Color getTabBackground( int tabPlacement, int tabIndex, boolean isSelected ) {
|
||||||
boolean enabled = tabPane.isEnabled();
|
boolean enabled = tabPane.isEnabled();
|
||||||
Color background = enabled && tabPane.isEnabledAt( tabIndex ) && getRolloverTab() == tabIndex
|
return enabled && tabPane.isEnabledAt( tabIndex ) && getRolloverTab() == tabIndex
|
||||||
? hoverColor
|
? hoverColor
|
||||||
: (enabled && isSelected && FlatUIUtils.isPermanentFocusOwner( tabPane )
|
: (enabled && isSelected && FlatUIUtils.isPermanentFocusOwner( tabPane )
|
||||||
? focusColor
|
? focusColor
|
||||||
: (selectedBackground != null && enabled && isSelected
|
: (selectedBackground != null && enabled && isSelected
|
||||||
? selectedBackground
|
? selectedBackground
|
||||||
: tabPane.getBackgroundAt( tabIndex )));
|
: tabPane.getBackgroundAt( tabIndex )));
|
||||||
return background;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1019,57 +997,59 @@ public class FlatTabbedPaneUI
|
|||||||
{
|
{
|
||||||
// paint tab separators
|
// paint tab separators
|
||||||
if( clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_TAB_SEPARATORS, showTabSeparators ) &&
|
if( clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_TAB_SEPARATORS, showTabSeparators ) &&
|
||||||
!isLastInRun( tabIndex ) ) {
|
!isLastInRun( tabIndex ) )
|
||||||
|
{
|
||||||
if( clientPropertyBoolean( tabPane, TABBED_PANE_ACTIVE_TAB_BORDER, activeTabBorder ) ) {
|
if( clientPropertyBoolean( tabPane, TABBED_PANE_ACTIVE_TAB_BORDER, activeTabBorder ) ) {
|
||||||
// Some separators need to be omitted when activeTabBorder is enabled
|
// Some separators need to be omitted when activeTabBorder is enabled
|
||||||
int selectedIndex = tabPane.getSelectedIndex();
|
int selectedIndex = tabPane.getSelectedIndex();
|
||||||
if (tabIndex != selectedIndex - 1 && tabIndex != selectedIndex) {
|
if( tabIndex != selectedIndex - 1 && tabIndex != selectedIndex )
|
||||||
paintTabSeparator( g, tabPlacement, x, y, w, h );
|
paintTabSeparator( g, tabPlacement, x, y, w, h );
|
||||||
}
|
} else
|
||||||
} else {
|
|
||||||
paintTabSeparator( g, tabPlacement, x, y, w, h );
|
paintTabSeparator( g, tabPlacement, x, y, w, h );
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// paint active tab border
|
// paint active tab border
|
||||||
if ( clientPropertyBoolean( tabPane, TABBED_PANE_ACTIVE_TAB_BORDER, activeTabBorder ) && isSelected) {
|
if( isSelected && clientPropertyBoolean( tabPane, TABBED_PANE_ACTIVE_TAB_BORDER, activeTabBorder ) )
|
||||||
paintActiveTabBorder(g, tabPlacement, tabIndex, x, y, w, h);
|
paintSelectedTabBorder( g, tabPlacement, tabIndex, x, y, w, h );
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paintActiveTabBorder( Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h) {
|
protected void paintSelectedTabBorder( Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h ) {
|
||||||
float borderWidth = scale(contentSeparatorHeight);
|
Graphics2D g2 = (Graphics2D) g;
|
||||||
|
|
||||||
|
float borderWidth = scale( (float) contentSeparatorHeight );
|
||||||
g.setColor( (tabSeparatorColor != null) ? tabSeparatorColor : contentAreaColor );
|
g.setColor( (tabSeparatorColor != null) ? tabSeparatorColor : contentAreaColor );
|
||||||
|
|
||||||
switch( tabPlacement ) {
|
switch( tabPlacement ) {
|
||||||
default:
|
default:
|
||||||
case TOP:
|
case TOP:
|
||||||
case BOTTOM:
|
case BOTTOM:
|
||||||
((Graphics2D)g).fill( new Rectangle2D.Float( x, y, borderWidth, h) );
|
// paint left and right tab border
|
||||||
((Graphics2D)g).fill( new Rectangle2D.Float( x + w - borderWidth, y, borderWidth, h) );
|
g2.fill( new Rectangle2D.Float( x, y, borderWidth, h ) );
|
||||||
|
g2.fill( new Rectangle2D.Float( x + w - borderWidth, y, borderWidth, h ) );
|
||||||
break;
|
break;
|
||||||
case LEFT:
|
case LEFT:
|
||||||
case RIGHT:
|
case RIGHT:
|
||||||
((Graphics2D)g).fill( new Rectangle2D.Float( x, y, w, borderWidth) );
|
// paint top and bottom tab border
|
||||||
((Graphics2D)g).fill( new Rectangle2D.Float( x, y + h - borderWidth, w, borderWidth) );
|
g2.fill( new Rectangle2D.Float( x, y, w, borderWidth ) );
|
||||||
|
g2.fill( new Rectangle2D.Float( x, y + h - borderWidth, w, borderWidth ) );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tabSelectionHeight <= 0) {
|
if( tabSelectionHeight <= 0 ) {
|
||||||
//If there is no tab selection indicator, paint a top border as well
|
//If there is no tab selection indicator, paint a top border as well
|
||||||
switch( tabPlacement ) {
|
switch( tabPlacement ) {
|
||||||
default:
|
default:
|
||||||
case TOP:
|
case TOP:
|
||||||
((Graphics2D)g).fill( new Rectangle2D.Float( x, y, w, borderWidth) );
|
g2.fill( new Rectangle2D.Float( x, y, w, borderWidth ) );
|
||||||
break;
|
break;
|
||||||
case BOTTOM:
|
case BOTTOM:
|
||||||
((Graphics2D)g).fill( new Rectangle2D.Float( x, y + h - borderWidth, w, borderWidth) );
|
g2.fill( new Rectangle2D.Float( x, y + h - borderWidth, w, borderWidth ) );
|
||||||
break;
|
break;
|
||||||
case LEFT:
|
case LEFT:
|
||||||
((Graphics2D)g).fill( new Rectangle2D.Float( x, y, borderWidth, h) );
|
g2.fill( new Rectangle2D.Float( x, y, borderWidth, h ) );
|
||||||
break;
|
break;
|
||||||
case RIGHT:
|
case RIGHT:
|
||||||
((Graphics2D)g).fill( new Rectangle2D.Float( x + w - borderWidth, y, borderWidth, h) );
|
g2.fill( new Rectangle2D.Float( x + w - borderWidth, y, borderWidth, h ) );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1118,49 +1098,50 @@ public class FlatTabbedPaneUI
|
|||||||
g.setColor( tabPane.isEnabled() ? underlineColor : disabledUnderlineColor );
|
g.setColor( tabPane.isEnabled() ? underlineColor : disabledUnderlineColor );
|
||||||
|
|
||||||
// paint underline selection
|
// paint underline selection
|
||||||
|
boolean atBottom = !clientPropertyBoolean( tabPane, TABBED_PANE_ACTIVE_TAB_BORDER, activeTabBorder );
|
||||||
Insets contentInsets = getContentBorderInsets( tabPlacement );
|
Insets contentInsets = getContentBorderInsets( tabPlacement );
|
||||||
int tabSelectionHeight = scale( this.tabSelectionHeight );
|
int tabSelectionHeight = scale( this.tabSelectionHeight );
|
||||||
|
int sx, sy;
|
||||||
switch( tabPlacement ) {
|
switch( tabPlacement ) {
|
||||||
case TOP:
|
case TOP:
|
||||||
default:
|
default:
|
||||||
int sy;
|
sy = atBottom ? (y + h + contentInsets.top - tabSelectionHeight) : y;
|
||||||
if ( clientPropertyBoolean( tabPane, TABBED_PANE_ACTIVE_TAB_BORDER, activeTabBorder ))
|
|
||||||
sy = y;
|
|
||||||
else
|
|
||||||
sy = y + h + contentInsets.top - tabSelectionHeight;
|
|
||||||
|
|
||||||
g.fillRect( x, sy, w, tabSelectionHeight );
|
g.fillRect( x, sy, w, tabSelectionHeight );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BOTTOM:
|
case BOTTOM:
|
||||||
if ( clientPropertyBoolean( tabPane, TABBED_PANE_ACTIVE_TAB_BORDER, activeTabBorder )) {
|
sy = atBottom ? (y - contentInsets.bottom) : (y + h - tabSelectionHeight);
|
||||||
g.fillRect( x, y + h - tabSelectionHeight, w, tabSelectionHeight );
|
g.fillRect( x, sy, w, tabSelectionHeight );
|
||||||
} else {
|
|
||||||
g.fillRect( x, y - contentInsets.bottom, w, tabSelectionHeight );
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LEFT:
|
case LEFT:
|
||||||
int sx;
|
sx = atBottom ? (x + w + contentInsets.left - tabSelectionHeight) : x;
|
||||||
if ( clientPropertyBoolean( tabPane, TABBED_PANE_ACTIVE_TAB_BORDER, activeTabBorder )) {
|
|
||||||
sx = x;
|
|
||||||
} else {
|
|
||||||
sx = x + w + contentInsets.left - tabSelectionHeight;
|
|
||||||
}
|
|
||||||
g.fillRect( sx, y, tabSelectionHeight, h );
|
g.fillRect( sx, y, tabSelectionHeight, h );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RIGHT:
|
case RIGHT:
|
||||||
if ( clientPropertyBoolean( tabPane, TABBED_PANE_ACTIVE_TAB_BORDER, activeTabBorder )) {
|
sx = atBottom ? (x - contentInsets.right) : (x + w - tabSelectionHeight);
|
||||||
g.fillRect( x + w - tabSelectionHeight, y, tabSelectionHeight, h );
|
g.fillRect( sx, y, tabSelectionHeight, h );
|
||||||
} else {
|
|
||||||
g.fillRect( x - contentInsets.right, y, tabSelectionHeight, h );
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Rectangle getContentBorderBounds(int tabPlacement) {
|
/**
|
||||||
|
* Actually does nearly the same as super.paintContentBorder() but
|
||||||
|
* - not using UIManager.getColor("TabbedPane.contentAreaColor") to be GUI builder friendly
|
||||||
|
* - tabsOverlapBorder is always true
|
||||||
|
* - paint full border (if enabled)
|
||||||
|
* - not invoking paintContentBorder*Edge() methods
|
||||||
|
* - repaint selection
|
||||||
|
* - painting active tab border style
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void paintContentBorder( Graphics g, int tabPlacement, int selectedIndex ) {
|
||||||
|
if( tabPane.getTabCount() <= 0 ||
|
||||||
|
contentSeparatorHeight == 0 ||
|
||||||
|
!clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, true ) )
|
||||||
|
return;
|
||||||
|
|
||||||
Insets insets = tabPane.getInsets();
|
Insets insets = tabPane.getInsets();
|
||||||
Insets tabAreaInsets = getTabAreaInsets( tabPlacement );
|
Insets tabAreaInsets = getTabAreaInsets( tabPlacement );
|
||||||
|
|
||||||
@@ -1195,126 +1176,56 @@ public class FlatTabbedPaneUI
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Rectangle(x, y, w, h);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Actually does nearly the same as super.paintContentBorder() but
|
|
||||||
* - not using UIManager.getColor("TabbedPane.contentAreaColor") to be GUI builder friendly
|
|
||||||
* - tabsOverlapBorder is always true
|
|
||||||
* - paint full border (if enabled)
|
|
||||||
* - not invoking paintContentBorder*Edge() methods
|
|
||||||
* - repaint selection
|
|
||||||
* - painting active tab border style
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void paintContentBorder( Graphics g, int tabPlacement, int selectedIndex ) {
|
|
||||||
if( tabPane.getTabCount() <= 0 ||
|
|
||||||
contentSeparatorHeight == 0 ||
|
|
||||||
!clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, true ) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
Rectangle contentBorderBounds = getContentBorderBounds(tabPlacement);
|
|
||||||
int x = contentBorderBounds.x;
|
|
||||||
int y = contentBorderBounds.y;
|
|
||||||
int w = contentBorderBounds.width;
|
|
||||||
int h = contentBorderBounds.height;
|
|
||||||
|
|
||||||
int csh = scale( contentSeparatorHeight );
|
|
||||||
|
|
||||||
// compute insets for separator or full border
|
// compute insets for separator or full border
|
||||||
boolean hasFullBorder = clientPropertyBoolean( tabPane, TABBED_PANE_HAS_FULL_BORDER, this.hasFullBorder );
|
boolean hasFullBorder = clientPropertyBoolean( tabPane, TABBED_PANE_HAS_FULL_BORDER, this.hasFullBorder );
|
||||||
int sh = scale( contentSeparatorHeight * 100 ); // multiply by 100 because rotateInsets() does not use floats
|
int sh = scale( contentSeparatorHeight * 100 ); // multiply by 100 because rotateInsets() does not use floats
|
||||||
Insets ci = new Insets( 0, 0, 0, 0 );
|
Insets ci = new Insets( 0, 0, 0, 0 );
|
||||||
rotateInsets( hasFullBorder ? new Insets( sh, sh, sh, sh ) : new Insets( sh, 0, 0, 0 ), ci, tabPlacement );
|
rotateInsets( hasFullBorder ? new Insets( sh, sh, sh, sh ) : new Insets( sh, 0, 0, 0 ), ci, tabPlacement );
|
||||||
|
|
||||||
// paint content separator or full border
|
// create path for content separator or full border
|
||||||
Area area = new Area();
|
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||||
|
path.append( new Rectangle2D.Float( x, y, w, h ), false );
|
||||||
|
path.append( new Rectangle2D.Float( x + (ci.left / 100f), y + (ci.top / 100f),
|
||||||
|
w - (ci.left / 100f) - (ci.right / 100f), h - (ci.top / 100f) - (ci.bottom / 100f) ), false );
|
||||||
|
|
||||||
if ( clientPropertyBoolean( tabPane, TABBED_PANE_ACTIVE_TAB_BORDER, activeTabBorder ) ) {
|
// add gap for selected tab to path
|
||||||
// Active tab border style
|
if( clientPropertyBoolean( tabPane, TABBED_PANE_ACTIVE_TAB_BORDER, activeTabBorder ) ) {
|
||||||
int ax = rects[selectedIndex].x;
|
float csh = scale( (float) contentSeparatorHeight );
|
||||||
int ay = rects[selectedIndex].y;
|
|
||||||
int aw = rects[selectedIndex].width;
|
|
||||||
int ah = rects[selectedIndex].height;
|
|
||||||
|
|
||||||
int scrollOffsetX = 0;
|
Rectangle tabRect = getTabBounds( tabPane, selectedIndex );
|
||||||
int scrollOffsetY = 0;
|
Rectangle2D.Float innerTabRect = new Rectangle2D.Float( tabRect.x + csh, tabRect.y + csh,
|
||||||
int tabViewportOffsetX = 0;
|
tabRect.width - (csh * 2), tabRect.height - (csh * 2) );
|
||||||
int tabViewportOffsetY = 0;
|
|
||||||
if (tabViewport != null && isScrollTabLayout()) {
|
|
||||||
scrollOffsetX = tabViewport.getViewRect().x;
|
|
||||||
scrollOffsetY = tabViewport.getViewRect().y;
|
|
||||||
tabViewportOffsetX = tabViewport.getX();
|
|
||||||
tabViewportOffsetY = tabViewport.getY();
|
|
||||||
}
|
|
||||||
|
|
||||||
int gapX;
|
|
||||||
int gapY;
|
|
||||||
int gapW;
|
|
||||||
int gapH;
|
|
||||||
switch( tabPlacement ) {
|
|
||||||
default:
|
|
||||||
case TOP:
|
|
||||||
gapX = x + ax + csh + (-scrollOffsetX + tabViewportOffsetX);
|
|
||||||
gapY = y;
|
|
||||||
gapW = aw - csh*2;
|
|
||||||
gapH = csh;
|
|
||||||
break;
|
|
||||||
case BOTTOM:
|
|
||||||
gapX = x + ax + csh + (-scrollOffsetX + tabViewportOffsetX);
|
|
||||||
gapY = h - csh;
|
|
||||||
gapW = aw - csh*2;
|
|
||||||
gapH = csh;
|
|
||||||
break;
|
|
||||||
case LEFT:
|
|
||||||
gapX = x;
|
|
||||||
gapY = y + ay + csh + (-scrollOffsetY + tabViewportOffsetY);
|
|
||||||
gapW = csh;
|
|
||||||
gapH = ah - csh*2;
|
|
||||||
break;
|
|
||||||
case RIGHT:
|
|
||||||
gapX = w - csh;
|
|
||||||
gapY = y + ay + csh + (-scrollOffsetY + tabViewportOffsetY);
|
|
||||||
gapW = csh;
|
|
||||||
gapH = ah - csh*2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
area.add( new Area( new Rectangle2D.Float( x, y, w, h ) ) );
|
|
||||||
area.subtract( new Area( new Rectangle2D.Float( gapX, gapY, gapW, gapH) ) );
|
|
||||||
|
|
||||||
Color activeTabAreaBackground = determineTabBackgroundColor(tabPlacement, selectedIndex, true);
|
|
||||||
g.setColor( activeTabAreaBackground );
|
|
||||||
((Graphics2D)g).fill( new Rectangle2D.Float( gapX, gapY, gapW, gapH) );
|
|
||||||
|
|
||||||
// Ensure that the separator outside the tabViewport is present (doesn't get cutoff by the active tab)
|
// Ensure that the separator outside the tabViewport is present (doesn't get cutoff by the active tab)
|
||||||
// If left unsolved the active tab is "visible" in the separator (the gap) even when outside the viewport
|
// If left unsolved the active tab is "visible" in the separator (the gap) even when outside the viewport
|
||||||
if (tabViewport != null && isScrollTabLayout()) {
|
if( tabViewport != null )
|
||||||
switch( tabPlacement ) {
|
Rectangle2D.intersect( tabViewport.getBounds(), innerTabRect, innerTabRect );
|
||||||
default:
|
|
||||||
case TOP:
|
Rectangle2D.Float gap = null;
|
||||||
case BOTTOM:
|
if( isHorizontalTabPlacement() ) {
|
||||||
area.add( new Area( new Rectangle2D.Float( x, y, tabViewport.getX() - x, h ) ) );
|
if( innerTabRect.width > 0 ) {
|
||||||
area.add( new Area( new Rectangle2D.Float( tabViewport.getX() + tabViewport.getWidth(), y, w - tabViewport.getX() - x, h ) ) );
|
float y2 = (tabPlacement == TOP) ? y : y + h - csh;
|
||||||
break;
|
gap = new Rectangle2D.Float( innerTabRect.x, y2, innerTabRect.width, csh );
|
||||||
case LEFT:
|
}
|
||||||
case RIGHT:
|
} else {
|
||||||
area.add( new Area( new Rectangle2D.Float( x, y, w, tabViewport.getY() - y ) ) );
|
if( innerTabRect.height > 0 ) {
|
||||||
area.add( new Area( new Rectangle2D.Float( x, tabViewport.getY() + tabViewport.getHeight(), w, h - tabViewport.getY() - y ) ) );
|
float x2 = (tabPlacement == LEFT) ? x : x + w - csh;
|
||||||
break;
|
gap = new Rectangle2D.Float( x2, innerTabRect.y, csh, innerTabRect.height );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
area.add( new Area( new Rectangle2D.Float(x, y, w, h) ) );
|
if( gap != null ) {
|
||||||
|
path.append( gap, false );
|
||||||
|
|
||||||
|
// fill gap in case that the tab is colored (e.g. focused or hover)
|
||||||
|
g.setColor( getTabBackground( tabPlacement, selectedIndex, true ) );
|
||||||
|
((Graphics2D)g).fill( gap );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle2D.Float r = new Rectangle2D.Float( x + (ci.left / 100f), y + (ci.top / 100f),
|
// paint content separator or full border
|
||||||
w - (ci.left / 100f) - (ci.right / 100f), h - (ci.top / 100f) - (ci.bottom / 100f) );
|
|
||||||
area.subtract( new Area( r ) );
|
|
||||||
|
|
||||||
g.setColor( contentAreaColor );
|
g.setColor( contentAreaColor );
|
||||||
((Graphics2D) g).fill( area );
|
((Graphics2D)g).fill( path );
|
||||||
|
|
||||||
// repaint selection in scroll-tab-layout because it may be painted before
|
// repaint selection in scroll-tab-layout because it may be painted before
|
||||||
// the content border was painted (from BasicTabbedPaneUI$ScrollableTabPanel)
|
// the content border was painted (from BasicTabbedPaneUI$ScrollableTabPanel)
|
||||||
@@ -2361,11 +2272,12 @@ public class FlatTabbedPaneUI
|
|||||||
|
|
||||||
private class Handler
|
private class Handler
|
||||||
implements MouseListener, MouseMotionListener, PropertyChangeListener,
|
implements MouseListener, MouseMotionListener, PropertyChangeListener,
|
||||||
ChangeListener, ComponentListener, ContainerListener
|
ChangeListener, ComponentListener, ContainerListener, FocusListener
|
||||||
{
|
{
|
||||||
MouseListener mouseDelegate;
|
MouseListener mouseDelegate;
|
||||||
PropertyChangeListener propertyChangeDelegate;
|
PropertyChangeListener propertyChangeDelegate;
|
||||||
ChangeListener changeDelegate;
|
ChangeListener changeDelegate;
|
||||||
|
FocusListener focusDelegate;
|
||||||
|
|
||||||
private final PropertyChangeListener contentListener = this::contentPropertyChange;
|
private final PropertyChangeListener contentListener = this::contentPropertyChange;
|
||||||
|
|
||||||
@@ -2534,6 +2446,9 @@ public class FlatTabbedPaneUI
|
|||||||
|
|
||||||
case TABBED_PANE_SHOW_TAB_SEPARATORS:
|
case TABBED_PANE_SHOW_TAB_SEPARATORS:
|
||||||
case TABBED_PANE_ACTIVE_TAB_BORDER:
|
case TABBED_PANE_ACTIVE_TAB_BORDER:
|
||||||
|
tabPane.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
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_HIDE_TAB_AREA_WITH_ONE_TAB:
|
case TABBED_PANE_HIDE_TAB_AREA_WITH_ONE_TAB:
|
||||||
@@ -2625,6 +2540,20 @@ public class FlatTabbedPaneUI
|
|||||||
if( !(c instanceof UIResource) )
|
if( !(c instanceof UIResource) )
|
||||||
c.removePropertyChangeListener( contentListener );
|
c.removePropertyChangeListener( contentListener );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- interface FocusListener ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusGained( FocusEvent e ) {
|
||||||
|
focusDelegate.focusGained( e );
|
||||||
|
repaintTab( tabPane.getSelectedIndex() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusLost( FocusEvent e ) {
|
||||||
|
focusDelegate.focusLost( e );
|
||||||
|
repaintTab( tabPane.getSelectedIndex() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class FlatTabbedPaneLayout -----------------------------------------
|
//---- class FlatTabbedPaneLayout -----------------------------------------
|
||||||
|
|||||||
@@ -17,13 +17,11 @@
|
|||||||
package com.formdev.flatlaf.demo;
|
package com.formdev.flatlaf.demo;
|
||||||
|
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import javax.swing.*;
|
import javax.swing.SwingUtilities;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
import com.formdev.flatlaf.extras.FlatInspector;
|
import com.formdev.flatlaf.extras.FlatInspector;
|
||||||
import com.formdev.flatlaf.extras.FlatUIDefaultsInspector;
|
import com.formdev.flatlaf.extras.FlatUIDefaultsInspector;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_ACTIVE_TAB_BORDER;
|
|
||||||
import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_HAS_FULL_BORDER;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
|
|||||||
Reference in New Issue
Block a user