mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-12 23:07:15 -06:00
Merge PR #703: TabbedPane: support rounded underline selection and rounded card tabs
This commit is contained in:
@@ -49,6 +49,7 @@ import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.event.MouseMotionListener;
|
||||
import java.awt.event.MouseWheelEvent;
|
||||
import java.awt.geom.Area;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
@@ -143,6 +144,11 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
* @uiDefault TabbedPane.tabHeight int
|
||||
* @uiDefault TabbedPane.tabSelectionHeight int
|
||||
* @uiDefault TabbedPane.cardTabSelectionHeight int
|
||||
* @uiDefault TabbedPane.tabArc int
|
||||
* @uiDefault TabbedPane.tabSelectionArc int
|
||||
* @uiDefault TabbedPane.cardTabArc int
|
||||
* @uiDefault TabbedPane.selectedInsets Insets
|
||||
* @uiDefault TabbedPane.tabSelectionInsets Insets
|
||||
* @uiDefault TabbedPane.contentSeparatorHeight int
|
||||
* @uiDefault TabbedPane.showTabSeparators boolean
|
||||
* @uiDefault TabbedPane.tabSeparatorsFullHeight boolean
|
||||
@@ -219,6 +225,11 @@ public class FlatTabbedPaneUI
|
||||
@Styleable protected int tabHeight;
|
||||
@Styleable protected int tabSelectionHeight;
|
||||
/** @since 2 */ @Styleable protected int cardTabSelectionHeight;
|
||||
/** @since 3.2 */ @Styleable protected int tabArc;
|
||||
/** @since 3.2 */ @Styleable protected int tabSelectionArc;
|
||||
/** @since 3.2 */ @Styleable protected int cardTabArc;
|
||||
/** @since 3.2 */ @Styleable protected Insets selectedInsets;
|
||||
/** @since 3.2 */ @Styleable protected Insets tabSelectionInsets;
|
||||
@Styleable protected int contentSeparatorHeight;
|
||||
@Styleable protected boolean showTabSeparators;
|
||||
@Styleable protected boolean tabSeparatorsFullHeight;
|
||||
@@ -344,6 +355,11 @@ public class FlatTabbedPaneUI
|
||||
tabHeight = UIManager.getInt( "TabbedPane.tabHeight" );
|
||||
tabSelectionHeight = UIManager.getInt( "TabbedPane.tabSelectionHeight" );
|
||||
cardTabSelectionHeight = UIManager.getInt( "TabbedPane.cardTabSelectionHeight" );
|
||||
tabArc = UIManager.getInt( "TabbedPane.tabArc" );
|
||||
tabSelectionArc = UIManager.getInt( "TabbedPane.tabSelectionArc" );
|
||||
cardTabArc = UIManager.getInt( "TabbedPane.cardTabArc" );
|
||||
selectedInsets = UIManager.getInsets( "TabbedPane.selectedInsets" );
|
||||
tabSelectionInsets = UIManager.getInsets( "TabbedPane.tabSelectionInsets" );
|
||||
contentSeparatorHeight = UIManager.getInt( "TabbedPane.contentSeparatorHeight" );
|
||||
showTabSeparators = UIManager.getBoolean( "TabbedPane.showTabSeparators" );
|
||||
tabSeparatorsFullHeight = UIManager.getBoolean( "TabbedPane.tabSeparatorsFullHeight" );
|
||||
@@ -1184,10 +1200,41 @@ public class FlatTabbedPaneUI
|
||||
protected void paintTabBackground( Graphics g, int tabPlacement, int tabIndex,
|
||||
int x, int y, int w, int h, boolean isSelected )
|
||||
{
|
||||
boolean isCard = (getTabType() == TAB_TYPE_CARD);
|
||||
|
||||
// fill whole tab background if tab is rounded or has insets
|
||||
if( (!isCard && tabArc > 0) ||
|
||||
(isCard && cardTabArc > 0) ||
|
||||
(!isCard && selectedInsets != null &&
|
||||
(selectedInsets.top != 0 || selectedInsets.left != 0 ||
|
||||
selectedInsets.bottom != 0 || selectedInsets.right != 0)) )
|
||||
{
|
||||
Color background = tabPane.getBackgroundAt( tabIndex );
|
||||
g.setColor( FlatUIUtils.deriveColor( background, tabPane.getBackground() ) );
|
||||
g.fillRect( x, y, w, h );
|
||||
}
|
||||
|
||||
// apply insets
|
||||
if( !isCard && selectedInsets != null ) {
|
||||
Insets insets = new Insets( 0, 0, 0, 0 );
|
||||
rotateInsets( selectedInsets, insets, tabPane.getTabPlacement() );
|
||||
|
||||
x += scale( insets.left );
|
||||
y += scale( insets.top );
|
||||
w -= scale( insets.left + insets.right );
|
||||
h -= scale( insets.top + insets.bottom );
|
||||
}
|
||||
|
||||
// paint tab background
|
||||
Color background = getTabBackground( tabPlacement, tabIndex, isSelected );
|
||||
g.setColor( FlatUIUtils.deriveColor( background, tabPane.getBackground() ) );
|
||||
g.fillRect( x, y, w, h );
|
||||
if( !isCard && tabArc > 0 ) {
|
||||
float arc = scale( (float) tabArc ) / 2f;
|
||||
FlatUIUtils.paintSelection( (Graphics2D) g, x, y, w, h, null, arc, arc, arc, arc, 0 );
|
||||
} else if( isCard && cardTabArc > 0 )
|
||||
((Graphics2D)g).fill( createCardTabOuterPath( tabPlacement, x, y, w, h ) );
|
||||
else
|
||||
g.fillRect( x, y, w, h );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@@ -1241,42 +1288,38 @@ public class FlatTabbedPaneUI
|
||||
protected void paintCardTabBorder( Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h ) {
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
|
||||
float borderWidth = scale( (float) contentSeparatorHeight );
|
||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||
path.append( createCardTabOuterPath( tabPlacement, x, y, w, h ), false );
|
||||
path.append( createCardTabInnerPath( tabPlacement, x, y, w, h ), false );
|
||||
|
||||
g.setColor( (tabSeparatorColor != null) ? tabSeparatorColor : contentAreaColor );
|
||||
g2.fill( path );
|
||||
}
|
||||
|
||||
/** @since 3.2 */
|
||||
protected Shape createCardTabOuterPath( int tabPlacement, int x, int y, int w, int h ) {
|
||||
float arc = scale( (float) cardTabArc ) / 2f;
|
||||
|
||||
switch( tabPlacement ) {
|
||||
default:
|
||||
case TOP:
|
||||
case BOTTOM:
|
||||
// paint left and right tab border
|
||||
g2.fill( new Rectangle2D.Float( x, y, borderWidth, h ) );
|
||||
g2.fill( new Rectangle2D.Float( x + w - borderWidth, y, borderWidth, h ) );
|
||||
break;
|
||||
case LEFT:
|
||||
case RIGHT:
|
||||
// paint top and bottom tab border
|
||||
g2.fill( new Rectangle2D.Float( x, y, w, borderWidth ) );
|
||||
g2.fill( new Rectangle2D.Float( x, y + h - borderWidth, w, borderWidth ) );
|
||||
break;
|
||||
case TOP: return FlatUIUtils.createRoundRectanglePath( x, y, w, h, arc, arc, 0, 0 );
|
||||
case BOTTOM: return FlatUIUtils.createRoundRectanglePath( x, y, w, h, 0, 0, arc, arc );
|
||||
case LEFT: return FlatUIUtils.createRoundRectanglePath( x, y, w, h, arc, 0, arc, 0 );
|
||||
case RIGHT: return FlatUIUtils.createRoundRectanglePath( x, y, w, h, 0, arc, 0, arc );
|
||||
}
|
||||
}
|
||||
|
||||
if( cardTabSelectionHeight <= 0 ) {
|
||||
// if there is no tab selection indicator, paint a top border as well
|
||||
switch( tabPlacement ) {
|
||||
default:
|
||||
case TOP:
|
||||
g2.fill( new Rectangle2D.Float( x, y, w, borderWidth ) );
|
||||
break;
|
||||
case BOTTOM:
|
||||
g2.fill( new Rectangle2D.Float( x, y + h - borderWidth, w, borderWidth ) );
|
||||
break;
|
||||
case LEFT:
|
||||
g2.fill( new Rectangle2D.Float( x, y, borderWidth, h ) );
|
||||
break;
|
||||
case RIGHT:
|
||||
g2.fill( new Rectangle2D.Float( x + w - borderWidth, y, borderWidth, h ) );
|
||||
break;
|
||||
}
|
||||
/** @since 3.2 */
|
||||
protected Shape createCardTabInnerPath( int tabPlacement, int x, int y, int w, int h ) {
|
||||
float bw = scale( (float) contentSeparatorHeight );
|
||||
float arc = (scale( (float) cardTabArc ) / 2f) - bw;
|
||||
|
||||
switch( tabPlacement ) {
|
||||
default:
|
||||
case TOP: return FlatUIUtils.createRoundRectanglePath( x + bw, y + bw, w - (bw * 2), h - bw, arc, arc, 0, 0 );
|
||||
case BOTTOM: return FlatUIUtils.createRoundRectanglePath( x + bw, y, w - (bw * 2), h - bw, 0, 0, arc, arc );
|
||||
case LEFT: return FlatUIUtils.createRoundRectanglePath( x + bw, y + bw, w - bw, h - (bw * 2), arc, 0, arc, 0 );
|
||||
case RIGHT: return FlatUIUtils.createRoundRectanglePath( x, y + bw, w - bw, h - (bw * 2), 0, arc, 0, arc );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1324,38 +1367,62 @@ public class FlatTabbedPaneUI
|
||||
? (isTabbedPaneOrChildFocused() ? underlineColor : inactiveUnderlineColor)
|
||||
: disabledUnderlineColor );
|
||||
|
||||
// paint underline selection
|
||||
boolean atBottom = (getTabType() != TAB_TYPE_CARD);
|
||||
boolean isCard = (getTabType() == TAB_TYPE_CARD);
|
||||
boolean atBottom = !isCard;
|
||||
Insets contentInsets = atBottom
|
||||
? ((!rotateTabRuns && runCount > 1 && !isScrollTabLayout() && getRunForTab( tabPane.getTabCount(), tabIndex ) > 0)
|
||||
? new Insets( 0, 0, 0, 0 )
|
||||
: getContentBorderInsets( tabPlacement ))
|
||||
: null;
|
||||
|
||||
int tabSelectionHeight = scale( atBottom ? this.tabSelectionHeight : cardTabSelectionHeight );
|
||||
int sx, sy;
|
||||
int tabSelectionHeight = scale( isCard ? cardTabSelectionHeight : this.tabSelectionHeight );
|
||||
float arc = scale( (float) (isCard ? cardTabArc : tabSelectionArc) ) / 2f;
|
||||
int sx = x, sy = y, sw = w, sh = h;
|
||||
switch( tabPlacement ) {
|
||||
case TOP:
|
||||
default:
|
||||
sy = atBottom ? (y + h + contentInsets.top - tabSelectionHeight) : y;
|
||||
g.fillRect( x, sy, w, tabSelectionHeight );
|
||||
sh = tabSelectionHeight;
|
||||
break;
|
||||
|
||||
case BOTTOM:
|
||||
sy = atBottom ? (y - contentInsets.bottom) : (y + h - tabSelectionHeight);
|
||||
g.fillRect( x, sy, w, tabSelectionHeight );
|
||||
sh = tabSelectionHeight;
|
||||
break;
|
||||
|
||||
case LEFT:
|
||||
sx = atBottom ? (x + w + contentInsets.left - tabSelectionHeight) : x;
|
||||
g.fillRect( sx, y, tabSelectionHeight, h );
|
||||
sw = tabSelectionHeight;
|
||||
break;
|
||||
|
||||
case RIGHT:
|
||||
sx = atBottom ? (x - contentInsets.right) : (x + w - tabSelectionHeight);
|
||||
g.fillRect( sx, y, tabSelectionHeight, h );
|
||||
sw = tabSelectionHeight;
|
||||
break;
|
||||
}
|
||||
|
||||
// apply insets
|
||||
if( !isCard && tabSelectionInsets != null ) {
|
||||
Insets insets = new Insets( 0, 0, 0, 0 );
|
||||
rotateInsets( tabSelectionInsets, insets, tabPane.getTabPlacement() );
|
||||
|
||||
sx += scale( insets.left );
|
||||
sy += scale( insets.top );
|
||||
sw -= scale( insets.left + insets.right );
|
||||
sh -= scale( insets.top + insets.bottom );
|
||||
}
|
||||
|
||||
// paint underline selection
|
||||
if( arc <= 0 )
|
||||
g.fillRect( sx, sy, sw, sh );
|
||||
else {
|
||||
if( isCard ) {
|
||||
Area area = new Area( createCardTabOuterPath( tabPlacement, x, y, w, h ) );
|
||||
area.intersect( new Area( new Rectangle2D.Float( sx, sy, sw, sh ) ) );
|
||||
((Graphics2D)g).fill( area );
|
||||
} else
|
||||
FlatUIUtils.paintSelection( (Graphics2D) g, sx, sy, sw, sh, null, arc, arc, arc, arc, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2.2 */
|
||||
|
||||
@@ -671,7 +671,12 @@ SplitPaneDivider.gripGap = 2
|
||||
|
||||
TabbedPane.tabHeight = 32
|
||||
TabbedPane.tabSelectionHeight = 3
|
||||
TabbedPane.cardTabSelectionHeight = 2
|
||||
TabbedPane.cardTabSelectionHeight = 3
|
||||
TabbedPane.tabArc = 0
|
||||
TabbedPane.tabSelectionArc = 0
|
||||
TabbedPane.cardTabArc = 12
|
||||
TabbedPane.selectedInsets = 0,0,0,0
|
||||
TabbedPane.tabSelectionInsets = 0,0,0,0
|
||||
TabbedPane.contentSeparatorHeight = 1
|
||||
TabbedPane.showTabSeparators = false
|
||||
TabbedPane.tabSeparatorsFullHeight = false
|
||||
|
||||
@@ -261,6 +261,13 @@ Spinner.buttonPressedArrowColor = lighten($Spinner.buttonArrowColor,20%,derived
|
||||
Spinner.buttonSeparatorWidth = 0
|
||||
|
||||
|
||||
#---- TabbedPane ----
|
||||
|
||||
TabbedPane.tabArc = $Button.arc
|
||||
TabbedPane.tabSelectionArc = 999
|
||||
TabbedPane.cardTabArc = $Button.arc
|
||||
|
||||
|
||||
#---- TextArea ---
|
||||
|
||||
TextArea.disabledBackground = @disabledComponentBackground
|
||||
|
||||
@@ -260,6 +260,13 @@ Spinner.buttonHoverArrowColor = lighten($Spinner.buttonArrowColor,20%,derived no
|
||||
Spinner.buttonPressedArrowColor = lighten($Spinner.buttonArrowColor,30%,derived noAutoInverse)
|
||||
|
||||
|
||||
#---- TabbedPane ----
|
||||
|
||||
TabbedPane.tabArc = $Button.arc
|
||||
TabbedPane.tabSelectionArc = 999
|
||||
TabbedPane.cardTabArc = $Button.arc
|
||||
|
||||
|
||||
#---- TextArea ---
|
||||
|
||||
TextArea.disabledBackground = @disabledComponentBackground
|
||||
|
||||
Reference in New Issue
Block a user