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)
This commit is contained in:
Karl Tauber
2021-03-13 17:08:47 +01:00
parent cee2211108
commit 30c7b442a8
10 changed files with 102 additions and 6 deletions

View File

@@ -9,8 +9,10 @@ FlatLaf Change Log
and embedded menu bar with all JREs, while still having native Windows 10 and embedded menu bar with all JREs, while still having native Windows 10
border drop shadows, resize behavior, window snapping and system window menu. border drop shadows, resize behavior, window snapping and system window menu.
(PR #267) (PR #267)
- Support right aligned components in `JFrame` title bar with embedded menu bar - Custom window decorations: Support right aligned components in `JFrame` title
(using `Box.createHorizontalGlue()`). (PR #268) 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 #### Fixed bugs

View File

@@ -83,6 +83,9 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TitlePane.titleMargins Insets * @uiDefault TitlePane.titleMargins Insets
* @uiDefault TitlePane.menuBarEmbedded boolean * @uiDefault TitlePane.menuBarEmbedded boolean
* @uiDefault TitlePane.buttonMaximizedHeight int * @uiDefault TitlePane.buttonMaximizedHeight int
* @uiDefault TitlePane.centerTitle boolean
* @uiDefault TitlePane.centerTitleIfMenuBarEmbedded boolean
* @uiDefault TitlePane.menuBarTitleGap int
* @uiDefault TitlePane.closeIcon Icon * @uiDefault TitlePane.closeIcon Icon
* @uiDefault TitlePane.iconifyIcon Icon * @uiDefault TitlePane.iconifyIcon Icon
* @uiDefault TitlePane.maximizeIcon Icon * @uiDefault TitlePane.maximizeIcon Icon
@@ -102,6 +105,9 @@ public class FlatTitlePane
protected final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" ); protected final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" );
protected final int buttonMaximizedHeight = UIManager.getInt( "TitlePane.buttonMaximizedHeight" ); 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; protected final JRootPane rootPane;
@@ -146,9 +152,15 @@ public class FlatTitlePane
protected void addSubComponents() { protected void addSubComponents() {
leftPanel = new JPanel(); leftPanel = new JPanel();
iconLabel = new JLabel(); iconLabel = new JLabel();
titleLabel = new JLabel(); titleLabel = new JLabel() {
@Override
public void updateUI() {
setUI( new FlatTitleLabelUI() );
}
};
iconLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.iconMargins" ) ) ); iconLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.iconMargins" ) ) );
titleLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.titleMargins" ) ) ); titleLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.titleMargins" ) ) );
titleLabel.setHorizontalAlignment( SwingConstants.CENTER );
leftPanel.setLayout( new BoxLayout( leftPanel, BoxLayout.LINE_AXIS ) ); leftPanel.setLayout( new BoxLayout( leftPanel, BoxLayout.LINE_AXIS ) );
leftPanel.setOpaque( false ); leftPanel.setOpaque( false );
@@ -261,8 +273,6 @@ public class FlatTitlePane
restoreButton.setForeground( foreground ); restoreButton.setForeground( foreground );
closeButton.setForeground( foreground ); closeButton.setForeground( foreground );
titleLabel.setHorizontalAlignment( hasEmbeddedMenuBar ? SwingConstants.CENTER : SwingConstants.LEADING );
// this is necessary because hover/pressed colors are derived from background color // this is necessary because hover/pressed colors are derived from background color
iconifyButton.setBackground( background ); iconifyButton.setBackground( background );
maximizeButton.setBackground( background ); maximizeButton.setBackground( background );
@@ -472,6 +482,7 @@ public class FlatTitlePane
protected void menuBarLayouted() { protected void menuBarLayouted() {
updateNativeTitleBarHeightAndHitTestSpotsLater(); updateNativeTitleBarHeightAndHitTestSpotsLater();
revalidate();
} }
/*debug /*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 ------------------------------------------------------ //---- class Handler ------------------------------------------------------
protected class Handler protected class Handler

View File

@@ -119,6 +119,11 @@ public class FlatUIUtils
return (color != null) ? color : UIManager.getColor( defaultKey ); 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 ) { public static int getUIInt( String key, int defaultValue ) {
Object value = UIManager.get( key ); Object value = UIManager.get( key );
return (value instanceof Integer) ? (Integer) value : defaultValue; return (value instanceof Integer) ? (Integer) value : defaultValue;

View File

@@ -692,6 +692,9 @@ TitlePane.iconMargins = 3,8,3,8
TitlePane.titleMargins = 3,0,3,0 TitlePane.titleMargins = 3,0,3,0
TitlePane.buttonSize = 44,30 TitlePane.buttonSize = 44,30
TitlePane.buttonMaximizedHeight = 22 TitlePane.buttonMaximizedHeight = 22
TitlePane.centerTitle = false
TitlePane.centerTitleIfMenuBarEmbedded = true
TitlePane.menuBarTitleGap = 20
TitlePane.closeIcon = com.formdev.flatlaf.icons.FlatWindowCloseIcon TitlePane.closeIcon = com.formdev.flatlaf.icons.FlatWindowCloseIcon
TitlePane.iconifyIcon = com.formdev.flatlaf.icons.FlatWindowIconifyIcon TitlePane.iconifyIcon = com.formdev.flatlaf.icons.FlatWindowIconifyIcon
TitlePane.maximizeIcon = com.formdev.flatlaf.icons.FlatWindowMaximizeIcon TitlePane.maximizeIcon = com.formdev.flatlaf.icons.FlatWindowMaximizeIcon

View File

@@ -1126,6 +1126,8 @@ TitlePane.buttonHoverBackground #484c4f com.formdev.flatlaf.util.DerivedColor
TitlePane.buttonMaximizedHeight 22 TitlePane.buttonMaximizedHeight 22
TitlePane.buttonPressedBackground #616569 com.formdev.flatlaf.util.DerivedColor [UI] lighten(20% autoInverse) TitlePane.buttonPressedBackground #616569 com.formdev.flatlaf.util.DerivedColor [UI] lighten(20% autoInverse)
TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI] TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI]
TitlePane.centerTitle false
TitlePane.centerTitleIfMenuBarEmbedded true
TitlePane.closeHoverBackground #e81123 javax.swing.plaf.ColorUIResource [UI] TitlePane.closeHoverBackground #e81123 javax.swing.plaf.ColorUIResource [UI]
TitlePane.closeHoverForeground #ffffff javax.swing.plaf.ColorUIResource [UI] TitlePane.closeHoverForeground #ffffff javax.swing.plaf.ColorUIResource [UI]
TitlePane.closeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowCloseIcon [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.inactiveForeground #888888 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true TitlePane.menuBarEmbedded true
TitlePane.menuBarTitleGap 20
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI] TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.useWindowDecorations true TitlePane.useWindowDecorations true

View File

@@ -1131,6 +1131,8 @@ TitlePane.buttonHoverBackground #e6e6e6 com.formdev.flatlaf.util.DerivedColor
TitlePane.buttonMaximizedHeight 22 TitlePane.buttonMaximizedHeight 22
TitlePane.buttonPressedBackground #cccccc com.formdev.flatlaf.util.DerivedColor [UI] darken(20% autoInverse) TitlePane.buttonPressedBackground #cccccc com.formdev.flatlaf.util.DerivedColor [UI] darken(20% autoInverse)
TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI] TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI]
TitlePane.centerTitle false
TitlePane.centerTitleIfMenuBarEmbedded true
TitlePane.closeHoverBackground #e81123 javax.swing.plaf.ColorUIResource [UI] TitlePane.closeHoverBackground #e81123 javax.swing.plaf.ColorUIResource [UI]
TitlePane.closeHoverForeground #ffffff javax.swing.plaf.ColorUIResource [UI] TitlePane.closeHoverForeground #ffffff javax.swing.plaf.ColorUIResource [UI]
TitlePane.closeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowCloseIcon [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.inactiveForeground #8c8c8c javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true TitlePane.menuBarEmbedded true
TitlePane.menuBarTitleGap 20
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI] TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.useWindowDecorations true TitlePane.useWindowDecorations true

View File

@@ -1124,6 +1124,8 @@ TextPaneUI com.formdev.flatlaf.ui.FlatTextPaneUI
TitlePane.background #00ff00 javax.swing.plaf.ColorUIResource [UI] TitlePane.background #00ff00 javax.swing.plaf.ColorUIResource [UI]
TitlePane.buttonMaximizedHeight 22 TitlePane.buttonMaximizedHeight 22
TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI] TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI]
TitlePane.centerTitle false
TitlePane.centerTitleIfMenuBarEmbedded true
TitlePane.closeHoverBackground #e81123 javax.swing.plaf.ColorUIResource [UI] TitlePane.closeHoverBackground #e81123 javax.swing.plaf.ColorUIResource [UI]
TitlePane.closeHoverForeground #ffffff javax.swing.plaf.ColorUIResource [UI] TitlePane.closeHoverForeground #ffffff javax.swing.plaf.ColorUIResource [UI]
TitlePane.closeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowCloseIcon [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.inactiveForeground #ffffff javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true TitlePane.menuBarEmbedded true
TitlePane.menuBarTitleGap 20
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI] TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.useWindowDecorations true TitlePane.useWindowDecorations true

View File

@@ -189,6 +189,14 @@ public class FlatWindowDecorationsTest
menuBar.getMenu( menuCount - 1 ).setText( text ); 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() { private void resizableChanged() {
Window window = SwingUtilities.windowForComponent( this ); Window window = SwingUtilities.windowForComponent( this );
if( window instanceof Frame ) if( window instanceof Frame )
@@ -308,6 +316,8 @@ public class FlatWindowDecorationsTest
colorizeMenusCheckBox = new JCheckBox(); colorizeMenusCheckBox = new JCheckBox();
resizableCheckBox = new JCheckBox(); resizableCheckBox = new JCheckBox();
maximizedBoundsCheckBox = new JCheckBox(); maximizedBoundsCheckBox = new JCheckBox();
JPanel hSpacer1 = new JPanel(null);
JButton changeTitleButton = new JButton();
undecoratedCheckBox = new JCheckBox(); undecoratedCheckBox = new JCheckBox();
fullScreenCheckBox = new JCheckBox(); fullScreenCheckBox = new JCheckBox();
JLabel label1 = new JLabel(); JLabel label1 = new JLabel();
@@ -428,6 +438,12 @@ public class FlatWindowDecorationsTest
maximizedBoundsCheckBox.setText("maximized bounds (50,100, 1000,700)"); maximizedBoundsCheckBox.setText("maximized bounds (50,100, 1000,700)");
maximizedBoundsCheckBox.addActionListener(e -> maximizedBoundsChanged()); maximizedBoundsCheckBox.addActionListener(e -> maximizedBoundsChanged());
add(maximizedBoundsCheckBox, "cell 1 3"); 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 ----
undecoratedCheckBox.setText("undecorated"); undecoratedCheckBox.setText("undecorated");

View File

@@ -123,6 +123,18 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3" "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" ) { add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "undecoratedCheckBox" name: "undecoratedCheckBox"
"text": "undecorated" "text": "undecorated"
@@ -316,7 +328,7 @@ new FormModel {
} ) } )
}, new FormLayoutConstraints( null ) { }, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 ) "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 ) ) { add( new FormContainer( "javax.swing.JMenuBar", new FormLayoutManager( class javax.swing.JMenuBar ) ) {
name: "menuBar" name: "menuBar"

View File

@@ -836,6 +836,8 @@ TitlePane.buttonHoverBackground
TitlePane.buttonMaximizedHeight TitlePane.buttonMaximizedHeight
TitlePane.buttonPressedBackground TitlePane.buttonPressedBackground
TitlePane.buttonSize TitlePane.buttonSize
TitlePane.centerTitle
TitlePane.centerTitleIfMenuBarEmbedded
TitlePane.closeHoverBackground TitlePane.closeHoverBackground
TitlePane.closeHoverForeground TitlePane.closeHoverForeground
TitlePane.closeIcon TitlePane.closeIcon
@@ -850,6 +852,7 @@ TitlePane.inactiveBackground
TitlePane.inactiveForeground TitlePane.inactiveForeground
TitlePane.maximizeIcon TitlePane.maximizeIcon
TitlePane.menuBarEmbedded TitlePane.menuBarEmbedded
TitlePane.menuBarTitleGap
TitlePane.restoreIcon TitlePane.restoreIcon
TitlePane.titleMargins TitlePane.titleMargins
TitlePane.useWindowDecorations TitlePane.useWindowDecorations