support painting separator line between window title and content (issue #184)

This commit is contained in:
Karl Tauber
2020-10-14 22:08:20 +02:00
parent 4b7ef6e853
commit ad7ff2ba0b
5 changed files with 91 additions and 21 deletions

View File

@@ -3,6 +3,12 @@ FlatLaf Change Log
## 0.44-SNAPSHOT ## 0.44-SNAPSHOT
#### New features and improvements
- Support painting separator line between window title and content (use UI value
`TitlePane.borderColor`). (issue #184)
#### Fixed bugs #### Fixed bugs
- Custom window decorations: Not visible menu bar is now ignored in layout. - Custom window decorations: Not visible menu bar is now ignored in layout.

View File

@@ -20,9 +20,7 @@ import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets; import java.awt.Insets;
import java.awt.geom.Rectangle2D;
import javax.swing.JMenuBar; import javax.swing.JMenuBar;
import javax.swing.UIManager; import javax.swing.UIManager;
@@ -40,16 +38,8 @@ public class FlatMenuBarBorder
@Override @Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) { public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
Graphics2D g2 = (Graphics2D) g.create(); float lineHeight = scale( (float) 1 );
try { FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
float lineHeight = scale( (float) 1 );
FlatUIUtils.setRenderingHints( g2 );
g2.setColor( borderColor );
g2.fill( new Rectangle2D.Float( x, y + height - lineHeight, width, lineHeight ) );
} finally {
g2.dispose();
}
} }
@Override @Override

View File

@@ -37,6 +37,7 @@ import javax.swing.JMenuBar;
import javax.swing.JRootPane; import javax.swing.JRootPane;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.BorderUIResource; import javax.swing.plaf.BorderUIResource;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
@@ -45,6 +46,7 @@ import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.HiDPIUtils; import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JRootPane}. * Provides the Flat LaF UI delegate for {@link javax.swing.JRootPane}.
@@ -54,6 +56,7 @@ import com.formdev.flatlaf.util.SystemInfo;
* @uiDefault RootPane.border Border * @uiDefault RootPane.border Border
* @uiDefault RootPane.activeBorderColor Color * @uiDefault RootPane.activeBorderColor Color
* @uiDefault RootPane.inactiveBorderColor Color * @uiDefault RootPane.inactiveBorderColor Color
* @uiDefault TitlePane.borderColor Color optional
* *
* <!-- FlatWindowResizer --> * <!-- FlatWindowResizer -->
* *
@@ -71,6 +74,8 @@ public class FlatRootPaneUI
static final boolean canUseJBRCustomDecorations static final boolean canUseJBRCustomDecorations
= SystemInfo.isJetBrainsJVM_11_orLater && SystemInfo.isWindows_10_orLater; = SystemInfo.isJetBrainsJVM_11_orLater && SystemInfo.isWindows_10_orLater;
protected final Color borderColor = UIManager.getColor( "TitlePane.borderColor" );
protected JRootPane rootPane; protected JRootPane rootPane;
protected FlatTitlePane titlePane; protected FlatTitlePane titlePane;
protected FlatWindowResizer windowResizer; protected FlatWindowResizer windowResizer;
@@ -89,11 +94,21 @@ public class FlatRootPaneUI
if( rootPane.getWindowDecorationStyle() != JRootPane.NONE ) if( rootPane.getWindowDecorationStyle() != JRootPane.NONE )
installClientDecorations(); installClientDecorations();
else
installBorder();
if( canUseJBRCustomDecorations ) if( canUseJBRCustomDecorations )
JBRCustomDecorations.install( rootPane ); JBRCustomDecorations.install( rootPane );
} }
protected void installBorder() {
if( borderColor != null ) {
Border b = rootPane.getBorder();
if( b == null || b instanceof UIResource )
rootPane.setBorder( new FlatWindowTitleBorder( borderColor ) );
}
}
@Override @Override
public void uninstallUI( JComponent c ) { public void uninstallUI( JComponent c ) {
super.uninstallUI( c ); super.uninstallUI( c );
@@ -119,11 +134,8 @@ public class FlatRootPaneUI
} }
// enable dark window appearance on macOS when running in JetBrains Runtime // enable dark window appearance on macOS when running in JetBrains Runtime
if( SystemInfo.isJetBrainsJVM && SystemInfo.isMacOS_10_14_Mojave_orLater ) { if( SystemInfo.isJetBrainsJVM && SystemInfo.isMacOS_10_14_Mojave_orLater )
LookAndFeel laf = UIManager.getLookAndFeel(); c.putClientProperty( "jetbrains.awt.windowDarkAppearance", FlatLaf.isLafDark() );
boolean isDark = laf instanceof FlatLaf && ((FlatLaf)laf).isDark();
c.putClientProperty( "jetbrains.awt.windowDarkAppearance", isDark );
}
} }
protected void installClientDecorations() { protected void installClientDecorations() {
@@ -203,6 +215,8 @@ public class FlatRootPaneUI
uninstallClientDecorations(); uninstallClientDecorations();
if( rootPane.getWindowDecorationStyle() != JRootPane.NONE ) if( rootPane.getWindowDecorationStyle() != JRootPane.NONE )
installClientDecorations(); installClientDecorations();
else
installBorder();
break; break;
case FlatClientProperties.MENU_BAR_EMBEDDED: case FlatClientProperties.MENU_BAR_EMBEDDED:
@@ -373,4 +387,40 @@ public class FlatRootPaneUI
: false; : false;
} }
} }
//---- class FlatWindowTitleBorder ----------------------------------------
private static class FlatWindowTitleBorder
extends BorderUIResource.EmptyBorderUIResource
{
private final Color borderColor;
FlatWindowTitleBorder( Color borderColor ) {
super( 0, 0, 0, 0 );
this.borderColor = borderColor;
}
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
if( showBorder( c ) ) {
float lineHeight = UIScale.scale( (float) 1 );
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y, width, lineHeight );
}
}
@Override
public Insets getBorderInsets( Component c, Insets insets ) {
insets.set( showBorder( c ) ? 1 : 0, 0, 0, 0 );
return insets;
}
private boolean showBorder( Component c ) {
Container parent = c.getParent();
return
(parent instanceof JFrame &&
(((JFrame)parent).getJMenuBar() == null ||
!((JFrame)parent).getJMenuBar().isVisible())) ||
parent instanceof JDialog;
}
}
} }

View File

@@ -76,6 +76,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TitlePane.foreground Color * @uiDefault TitlePane.foreground Color
* @uiDefault TitlePane.inactiveForeground Color * @uiDefault TitlePane.inactiveForeground Color
* @uiDefault TitlePane.embeddedForeground Color * @uiDefault TitlePane.embeddedForeground Color
* @uiDefault TitlePane.borderColor Color optional
* @uiDefault TitlePane.iconSize Dimension * @uiDefault TitlePane.iconSize Dimension
* @uiDefault TitlePane.iconMargins Insets * @uiDefault TitlePane.iconMargins Insets
* @uiDefault TitlePane.titleMargins Insets * @uiDefault TitlePane.titleMargins Insets
@@ -97,6 +98,7 @@ public class FlatTitlePane
protected final Color activeForeground = UIManager.getColor( "TitlePane.foreground" ); protected final Color activeForeground = UIManager.getColor( "TitlePane.foreground" );
protected final Color inactiveForeground = UIManager.getColor( "TitlePane.inactiveForeground" ); protected final Color inactiveForeground = UIManager.getColor( "TitlePane.inactiveForeground" );
protected final Color embeddedForeground = UIManager.getColor( "TitlePane.embeddedForeground" ); protected final Color embeddedForeground = UIManager.getColor( "TitlePane.embeddedForeground" );
protected final Color borderColor = UIManager.getColor( "TitlePane.borderColor" );
protected final Insets menuBarMargins = UIManager.getInsets( "TitlePane.menuBarMargins" ); protected final Insets menuBarMargins = UIManager.getInsets( "TitlePane.menuBarMargins" );
protected final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" ); protected final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" );
@@ -422,6 +424,10 @@ public class FlatTitlePane
protected void menuBarChanged() { protected void menuBarChanged() {
menuBarPlaceholder.invalidate(); menuBarPlaceholder.invalidate();
// necessary for the case that an embedded menu bar is made invisible
// and a border color is specified
repaint();
// update title foreground color // update title foreground color
EventQueue.invokeLater( () -> { EventQueue.invokeLater( () -> {
activeChanged( window == null || window.isActive() ); activeChanged( window == null || window.isActive() );
@@ -662,12 +668,13 @@ debug*/
public Insets getBorderInsets( Component c, Insets insets ) { public Insets getBorderInsets( Component c, Insets insets ) {
super.getBorderInsets( c, insets ); super.getBorderInsets( c, insets );
// if menu bar is embedded, add bottom insets of menu bar border
Border menuBarBorder = getMenuBarBorder(); Border menuBarBorder = getMenuBarBorder();
if( menuBarBorder != null ) { if( menuBarBorder != null ) {
// if menu bar is embedded, add bottom insets of menu bar border
Insets menuBarInsets = menuBarBorder.getBorderInsets( c ); Insets menuBarInsets = menuBarBorder.getBorderInsets( c );
insets.bottom += menuBarInsets.bottom; insets.bottom += menuBarInsets.bottom;
} } else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) )
insets.bottom += UIScale.scale( 1 );
if( hasJBRCustomDecoration() ) if( hasJBRCustomDecoration() )
insets = FlatUIUtils.addInsets( insets, JBRWindowTopBorder.getInstance().getBorderInsets() ); insets = FlatUIUtils.addInsets( insets, JBRWindowTopBorder.getInstance().getBorderInsets() );
@@ -677,10 +684,16 @@ debug*/
@Override @Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) { public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
// if menu bar is embedded, paint menu bar border // paint bottom border
Border menuBarBorder = getMenuBarBorder(); Border menuBarBorder = getMenuBarBorder();
if( menuBarBorder != null ) if( menuBarBorder != null ) {
// if menu bar is embedded, paint menu bar border
menuBarBorder.paintBorder( c, g, x, y, width, height ); menuBarBorder.paintBorder( c, g, x, y, width, height );
} else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) ) {
// paint border between title pane and content if border color is specified
float lineHeight = UIScale.scale( (float) 1 );
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
}
if( hasJBRCustomDecoration() ) if( hasJBRCustomDecoration() )
JBRWindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height ); JBRWindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height );

View File

@@ -379,6 +379,17 @@ public class FlatUIUtils
return new RoundRectangle2D.Float( x, y, w, h, arc, arc ); return new RoundRectangle2D.Float( x, y, w, h, arc, arc );
} }
static void paintFilledRectangle( Graphics g, Color color, float x, float y, float w, float h ) {
Graphics2D g2 = (Graphics2D) g.create();
try {
FlatUIUtils.setRenderingHints( g2 );
g2.setColor( color );
g2.fill( new Rectangle2D.Float( x, y, w, h ) );
} finally {
g2.dispose();
}
}
/** /**
* Fill background with parent's background color because the visible component * Fill background with parent's background color because the visible component
* is smaller than its bounds (for the focus decoration). * is smaller than its bounds (for the focus decoration).