From 30c7b442a8682ed2fe52e573f7f768c19b071211 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Sat, 13 Mar 2021 17:08:47 +0100 Subject: [PATCH] Window decorations: - support customizing of window title alignment: left aligned or centered (default is left without embedded menubar and centered with embedded menubar) - improved centering of window title with embedded menubar (issue #252) --- CHANGELOG.md | 6 ++- .../com/formdev/flatlaf/ui/FlatTitlePane.java | 52 +++++++++++++++++-- .../com/formdev/flatlaf/ui/FlatUIUtils.java | 5 ++ .../com/formdev/flatlaf/FlatLaf.properties | 3 ++ .../uidefaults/FlatDarkLaf_1.8.0_202.txt | 3 ++ .../uidefaults/FlatLightLaf_1.8.0_202.txt | 3 ++ .../uidefaults/FlatTestLaf_1.8.0_202.txt | 3 ++ .../testing/FlatWindowDecorationsTest.java | 16 ++++++ .../testing/FlatWindowDecorationsTest.jfd | 14 ++++- .../flatlaf/themeeditor/FlatLafUIKeys.txt | 3 ++ 10 files changed, 102 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a0f54ca..04b73043 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,10 @@ FlatLaf Change Log and embedded menu bar with all JREs, while still having native Windows 10 border drop shadows, resize behavior, window snapping and system window menu. (PR #267) -- Support right aligned components in `JFrame` title bar with embedded menu bar - (using `Box.createHorizontalGlue()`). (PR #268) +- Custom window decorations: Support right aligned components in `JFrame` title + bar with embedded menu bar (using `Box.createHorizontalGlue()`). (PR #268) +- Custom window decorations: Improved centering of window title with embedded + menu bar. (issue #252) #### Fixed bugs 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 ffe0df7f..4c581be9 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 @@ -83,6 +83,9 @@ import com.formdev.flatlaf.util.UIScale; * @uiDefault TitlePane.titleMargins Insets * @uiDefault TitlePane.menuBarEmbedded boolean * @uiDefault TitlePane.buttonMaximizedHeight int + * @uiDefault TitlePane.centerTitle boolean + * @uiDefault TitlePane.centerTitleIfMenuBarEmbedded boolean + * @uiDefault TitlePane.menuBarTitleGap int * @uiDefault TitlePane.closeIcon Icon * @uiDefault TitlePane.iconifyIcon Icon * @uiDefault TitlePane.maximizeIcon Icon @@ -102,6 +105,9 @@ public class FlatTitlePane protected final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" ); protected final int buttonMaximizedHeight = UIManager.getInt( "TitlePane.buttonMaximizedHeight" ); + protected final boolean centerTitle = UIManager.getBoolean( "TitlePane.centerTitle" ); + protected final boolean centerTitleIfMenuBarEmbedded = FlatUIUtils.getUIBoolean( "TitlePane.centerTitleIfMenuBarEmbedded", true ); + protected final int menuBarTitleGap = FlatUIUtils.getUIInt( "TitlePane.menuBarTitleGap", 20 ); protected final JRootPane rootPane; @@ -146,9 +152,15 @@ public class FlatTitlePane protected void addSubComponents() { leftPanel = new JPanel(); iconLabel = new JLabel(); - titleLabel = new JLabel(); + titleLabel = new JLabel() { + @Override + public void updateUI() { + setUI( new FlatTitleLabelUI() ); + } + }; iconLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.iconMargins" ) ) ); titleLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.titleMargins" ) ) ); + titleLabel.setHorizontalAlignment( SwingConstants.CENTER ); leftPanel.setLayout( new BoxLayout( leftPanel, BoxLayout.LINE_AXIS ) ); leftPanel.setOpaque( false ); @@ -261,8 +273,6 @@ public class FlatTitlePane restoreButton.setForeground( foreground ); closeButton.setForeground( foreground ); - titleLabel.setHorizontalAlignment( hasEmbeddedMenuBar ? SwingConstants.CENTER : SwingConstants.LEADING ); - // this is necessary because hover/pressed colors are derived from background color iconifyButton.setBackground( background ); maximizeButton.setBackground( background ); @@ -472,6 +482,7 @@ public class FlatTitlePane protected void menuBarLayouted() { updateNativeTitleBarHeightAndHitTestSpotsLater(); + revalidate(); } /*debug @@ -798,6 +809,41 @@ debug*/ } } + //---- class FlatTitleLabelUI --------------------------------------------- + + protected class FlatTitleLabelUI + extends FlatLabelUI + { + @Override + protected void paintEnabledText( JLabel l, Graphics g, String s, int textX, int textY ) { + boolean hasEmbeddedMenuBar = hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() ); + int labelWidth = l.getWidth(); + int textWidth = labelWidth - (textX * 2); + int gap = UIScale.scale( menuBarTitleGap ); + + // The passed in textX coordinate is always to horizontally center the text within the label bounds. + // Modify textX so that the text is painted either centered within the window bounds or leading aligned. + boolean center = hasEmbeddedMenuBar ? centerTitleIfMenuBarEmbedded : centerTitle; + if( center ) { + // If window is wide enough, center title within window bounds. + // Otherwise leave it centered within free space (label bounds). + int centeredTextX = ((l.getParent().getWidth() - textWidth) / 2) - l.getX(); + if( centeredTextX >= gap && centeredTextX + textWidth <= labelWidth - gap ) + textX = centeredTextX; + } else { + // leading aligned + boolean leftToRight = getComponentOrientation().isLeftToRight(); + Insets insets = l.getInsets(); + int leadingInset = hasEmbeddedMenuBar ? gap : (leftToRight ? insets.left : insets.right); + int leadingTextX = leftToRight ? leadingInset : labelWidth - leadingInset - textWidth; + if( leftToRight ? leadingTextX < textX : leadingTextX > textX ) + textX = leadingTextX; + } + + super.paintEnabledText( l, g, s, textX, textY ); + } + } + //---- class Handler ------------------------------------------------------ protected class Handler 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 4030d43a..5e0eb5f8 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 @@ -119,6 +119,11 @@ public class FlatUIUtils return (color != null) ? color : UIManager.getColor( defaultKey ); } + public static boolean getUIBoolean( String key, boolean defaultValue ) { + Object value = UIManager.get( key ); + return (value instanceof Boolean) ? (Boolean) value : defaultValue; + } + public static int getUIInt( String key, int defaultValue ) { Object value = UIManager.get( key ); return (value instanceof Integer) ? (Integer) value : defaultValue; diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties index 45a66b3e..e055aa58 100644 --- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties +++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties @@ -692,6 +692,9 @@ TitlePane.iconMargins = 3,8,3,8 TitlePane.titleMargins = 3,0,3,0 TitlePane.buttonSize = 44,30 TitlePane.buttonMaximizedHeight = 22 +TitlePane.centerTitle = false +TitlePane.centerTitleIfMenuBarEmbedded = true +TitlePane.menuBarTitleGap = 20 TitlePane.closeIcon = com.formdev.flatlaf.icons.FlatWindowCloseIcon TitlePane.iconifyIcon = com.formdev.flatlaf.icons.FlatWindowIconifyIcon TitlePane.maximizeIcon = com.formdev.flatlaf.icons.FlatWindowMaximizeIcon diff --git a/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0_202.txt b/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0_202.txt index a4c45c88..8ac1f7b6 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0_202.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0_202.txt @@ -1126,6 +1126,8 @@ TitlePane.buttonHoverBackground #484c4f com.formdev.flatlaf.util.DerivedColor TitlePane.buttonMaximizedHeight 22 TitlePane.buttonPressedBackground #616569 com.formdev.flatlaf.util.DerivedColor [UI] lighten(20% autoInverse) TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI] +TitlePane.centerTitle false +TitlePane.centerTitleIfMenuBarEmbedded true TitlePane.closeHoverBackground #e81123 javax.swing.plaf.ColorUIResource [UI] TitlePane.closeHoverForeground #ffffff javax.swing.plaf.ColorUIResource [UI] TitlePane.closeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowCloseIcon [UI] @@ -1140,6 +1142,7 @@ TitlePane.inactiveBackground #303234 javax.swing.plaf.ColorUIResource [UI] TitlePane.inactiveForeground #888888 javax.swing.plaf.ColorUIResource [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.menuBarEmbedded true +TitlePane.menuBarTitleGap 20 TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI] TitlePane.useWindowDecorations true diff --git a/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0_202.txt b/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0_202.txt index f8da0584..d4ba22a4 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0_202.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0_202.txt @@ -1131,6 +1131,8 @@ TitlePane.buttonHoverBackground #e6e6e6 com.formdev.flatlaf.util.DerivedColor TitlePane.buttonMaximizedHeight 22 TitlePane.buttonPressedBackground #cccccc com.formdev.flatlaf.util.DerivedColor [UI] darken(20% autoInverse) TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI] +TitlePane.centerTitle false +TitlePane.centerTitleIfMenuBarEmbedded true TitlePane.closeHoverBackground #e81123 javax.swing.plaf.ColorUIResource [UI] TitlePane.closeHoverForeground #ffffff javax.swing.plaf.ColorUIResource [UI] TitlePane.closeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowCloseIcon [UI] @@ -1145,6 +1147,7 @@ TitlePane.inactiveBackground #ffffff javax.swing.plaf.ColorUIResource [UI] TitlePane.inactiveForeground #8c8c8c javax.swing.plaf.ColorUIResource [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.menuBarEmbedded true +TitlePane.menuBarTitleGap 20 TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI] TitlePane.useWindowDecorations true diff --git a/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0_202.txt b/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0_202.txt index 361f9b13..7b4676fa 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0_202.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0_202.txt @@ -1124,6 +1124,8 @@ TextPaneUI com.formdev.flatlaf.ui.FlatTextPaneUI TitlePane.background #00ff00 javax.swing.plaf.ColorUIResource [UI] TitlePane.buttonMaximizedHeight 22 TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI] +TitlePane.centerTitle false +TitlePane.centerTitleIfMenuBarEmbedded true TitlePane.closeHoverBackground #e81123 javax.swing.plaf.ColorUIResource [UI] TitlePane.closeHoverForeground #ffffff javax.swing.plaf.ColorUIResource [UI] TitlePane.closeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowCloseIcon [UI] @@ -1137,6 +1139,7 @@ TitlePane.inactiveBackground #008800 javax.swing.plaf.ColorUIResource [UI] TitlePane.inactiveForeground #ffffff javax.swing.plaf.ColorUIResource [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.menuBarEmbedded true +TitlePane.menuBarTitleGap 20 TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI] TitlePane.useWindowDecorations true diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.java index 9a835539..f6971662 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.java +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.java @@ -189,6 +189,14 @@ public class FlatWindowDecorationsTest menuBar.getMenu( menuCount - 1 ).setText( text ); } + private void changeTitle() { + Window window = SwingUtilities.windowForComponent( this ); + if( window instanceof Frame ) + ((Frame)window).setTitle( ((Frame)window).getTitle() + " bla" ); + else if( window instanceof Dialog ) + ((Dialog)window).setTitle( ((Dialog)window).getTitle() + " bla" ); + } + private void resizableChanged() { Window window = SwingUtilities.windowForComponent( this ); if( window instanceof Frame ) @@ -308,6 +316,8 @@ public class FlatWindowDecorationsTest colorizeMenusCheckBox = new JCheckBox(); resizableCheckBox = new JCheckBox(); maximizedBoundsCheckBox = new JCheckBox(); + JPanel hSpacer1 = new JPanel(null); + JButton changeTitleButton = new JButton(); undecoratedCheckBox = new JCheckBox(); fullScreenCheckBox = new JCheckBox(); JLabel label1 = new JLabel(); @@ -428,6 +438,12 @@ public class FlatWindowDecorationsTest maximizedBoundsCheckBox.setText("maximized bounds (50,100, 1000,700)"); maximizedBoundsCheckBox.addActionListener(e -> maximizedBoundsChanged()); add(maximizedBoundsCheckBox, "cell 1 3"); + add(hSpacer1, "cell 1 3,growx"); + + //---- changeTitleButton ---- + changeTitleButton.setText("Change title"); + changeTitleButton.addActionListener(e -> changeTitle()); + add(changeTitleButton, "cell 1 3"); //---- undecoratedCheckBox ---- undecoratedCheckBox.setText("undecorated"); diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.jfd b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.jfd index f1b87adc..91daff59 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.jfd +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.jfd @@ -123,6 +123,18 @@ new FormModel { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 1 3" } ) + add( new FormComponent( "com.jformdesigner.designer.wrapper.HSpacer" ) { + name: "hSpacer1" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 3,growx" + } ) + add( new FormComponent( "javax.swing.JButton" ) { + name: "changeTitleButton" + "text": "Change title" + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "changeTitle", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 3" + } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "undecoratedCheckBox" "text": "undecorated" @@ -316,7 +328,7 @@ new FormModel { } ) }, new FormLayoutConstraints( null ) { "location": new java.awt.Point( 0, 0 ) - "size": new java.awt.Dimension( 550, 440 ) + "size": new java.awt.Dimension( 580, 440 ) } ) add( new FormContainer( "javax.swing.JMenuBar", new FormLayoutManager( class javax.swing.JMenuBar ) ) { name: "menuBar" diff --git a/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt b/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt index 7a766b57..4a388aa5 100644 --- a/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt +++ b/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt @@ -836,6 +836,8 @@ TitlePane.buttonHoverBackground TitlePane.buttonMaximizedHeight TitlePane.buttonPressedBackground TitlePane.buttonSize +TitlePane.centerTitle +TitlePane.centerTitleIfMenuBarEmbedded TitlePane.closeHoverBackground TitlePane.closeHoverForeground TitlePane.closeIcon @@ -850,6 +852,7 @@ TitlePane.inactiveBackground TitlePane.inactiveForeground TitlePane.maximizeIcon TitlePane.menuBarEmbedded +TitlePane.menuBarTitleGap TitlePane.restoreIcon TitlePane.titleMargins TitlePane.useWindowDecorations