From ad7ff2ba0bd86153dba8db4db8ca0cfdeda818a7 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Wed, 14 Oct 2020 22:08:20 +0200 Subject: [PATCH] support painting separator line between window title and content (issue #184) --- CHANGELOG.md | 6 ++ .../formdev/flatlaf/ui/FlatMenuBarBorder.java | 14 +---- .../formdev/flatlaf/ui/FlatRootPaneUI.java | 60 +++++++++++++++++-- .../com/formdev/flatlaf/ui/FlatTitlePane.java | 21 +++++-- .../com/formdev/flatlaf/ui/FlatUIUtils.java | 11 ++++ 5 files changed, 91 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fc0b5da9..39344c387 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ FlatLaf Change Log ## 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 - Custom window decorations: Not visible menu bar is now ignored in layout. diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarBorder.java index b98ebe5f0..5701f935f 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarBorder.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarBorder.java @@ -20,9 +20,7 @@ import java.awt.Color; import java.awt.Component; import java.awt.Graphics; -import java.awt.Graphics2D; import java.awt.Insets; -import java.awt.geom.Rectangle2D; import javax.swing.JMenuBar; import javax.swing.UIManager; @@ -40,16 +38,8 @@ public class FlatMenuBarBorder @Override public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) { - Graphics2D g2 = (Graphics2D) g.create(); - try { - 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(); - } + float lineHeight = scale( (float) 1 ); + FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight ); } @Override diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRootPaneUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRootPaneUI.java index d29406efc..82098cda9 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRootPaneUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRootPaneUI.java @@ -37,6 +37,7 @@ import javax.swing.JRootPane; import javax.swing.LookAndFeel; import javax.swing.UIManager; +import javax.swing.border.Border; import javax.swing.plaf.BorderUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.UIResource; @@ -45,6 +46,7 @@ import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.util.HiDPIUtils; import com.formdev.flatlaf.util.SystemInfo; +import com.formdev.flatlaf.util.UIScale; /** * Provides the Flat LaF UI delegate for {@link javax.swing.JRootPane}. @@ -54,6 +56,7 @@ * @uiDefault RootPane.border Border * @uiDefault RootPane.activeBorderColor Color * @uiDefault RootPane.inactiveBorderColor Color + * @uiDefault TitlePane.borderColor Color optional * * * @@ -71,6 +74,8 @@ public class FlatRootPaneUI static final boolean canUseJBRCustomDecorations = SystemInfo.isJetBrainsJVM_11_orLater && SystemInfo.isWindows_10_orLater; + protected final Color borderColor = UIManager.getColor( "TitlePane.borderColor" ); + protected JRootPane rootPane; protected FlatTitlePane titlePane; protected FlatWindowResizer windowResizer; @@ -89,11 +94,21 @@ public void installUI( JComponent c ) { if( rootPane.getWindowDecorationStyle() != JRootPane.NONE ) installClientDecorations(); + else + installBorder(); if( canUseJBRCustomDecorations ) 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 public void uninstallUI( JComponent c ) { super.uninstallUI( c ); @@ -119,11 +134,8 @@ protected void installDefaults( JRootPane c ) { } // enable dark window appearance on macOS when running in JetBrains Runtime - if( SystemInfo.isJetBrainsJVM && SystemInfo.isMacOS_10_14_Mojave_orLater ) { - LookAndFeel laf = UIManager.getLookAndFeel(); - boolean isDark = laf instanceof FlatLaf && ((FlatLaf)laf).isDark(); - c.putClientProperty( "jetbrains.awt.windowDarkAppearance", isDark ); - } + if( SystemInfo.isJetBrainsJVM && SystemInfo.isMacOS_10_14_Mojave_orLater ) + c.putClientProperty( "jetbrains.awt.windowDarkAppearance", FlatLaf.isLafDark() ); } protected void installClientDecorations() { @@ -203,6 +215,8 @@ public void propertyChange( PropertyChangeEvent e ) { uninstallClientDecorations(); if( rootPane.getWindowDecorationStyle() != JRootPane.NONE ) installClientDecorations(); + else + installBorder(); break; case FlatClientProperties.MENU_BAR_EMBEDDED: @@ -373,4 +387,40 @@ protected boolean isWindowMaximized( Component c ) { : 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; + } + } } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java index 49026992e..ac13a2317 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java @@ -76,6 +76,7 @@ * @uiDefault TitlePane.foreground Color * @uiDefault TitlePane.inactiveForeground Color * @uiDefault TitlePane.embeddedForeground Color + * @uiDefault TitlePane.borderColor Color optional * @uiDefault TitlePane.iconSize Dimension * @uiDefault TitlePane.iconMargins Insets * @uiDefault TitlePane.titleMargins Insets @@ -97,6 +98,7 @@ public class FlatTitlePane protected final Color activeForeground = UIManager.getColor( "TitlePane.foreground" ); protected final Color inactiveForeground = UIManager.getColor( "TitlePane.inactiveForeground" ); 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 Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" ); @@ -422,6 +424,10 @@ protected Insets getMenuBarMargins() { protected void menuBarChanged() { 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 EventQueue.invokeLater( () -> { activeChanged( window == null || window.isActive() ); @@ -662,12 +668,13 @@ protected class FlatTitlePaneBorder public Insets getBorderInsets( Component c, Insets insets ) { super.getBorderInsets( c, insets ); - // if menu bar is embedded, add bottom insets of menu bar border Border menuBarBorder = getMenuBarBorder(); if( menuBarBorder != null ) { + // if menu bar is embedded, add bottom insets of menu bar border Insets menuBarInsets = menuBarBorder.getBorderInsets( c ); insets.bottom += menuBarInsets.bottom; - } + } else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) ) + insets.bottom += UIScale.scale( 1 ); if( hasJBRCustomDecoration() ) insets = FlatUIUtils.addInsets( insets, JBRWindowTopBorder.getInstance().getBorderInsets() ); @@ -677,10 +684,16 @@ public Insets getBorderInsets( Component c, Insets insets ) { @Override 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(); - if( menuBarBorder != null ) + if( menuBarBorder != null ) { + // if menu bar is embedded, paint menu bar border 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() ) JBRWindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height ); diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java index fb35c0642..b48e153b5 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java @@ -379,6 +379,17 @@ public static Shape createComponentRectangle( float x, float y, float w, float h 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 * is smaller than its bounds (for the focus decoration).