diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarUI.java index 273fb080..e167c368 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarUI.java @@ -18,8 +18,10 @@ package com.formdev.flatlaf.ui; import java.awt.Color; import java.awt.Component; +import java.awt.Container; import java.awt.Graphics; import java.awt.Insets; +import java.awt.LayoutManager; import java.awt.Window; import java.awt.event.ActionEvent; import java.beans.PropertyChangeListener; @@ -27,6 +29,7 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import javax.swing.AbstractAction; import javax.swing.ActionMap; +import javax.swing.BoxLayout; import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.JMenuBar; @@ -40,11 +43,13 @@ import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicMenuBarUI; +import javax.swing.plaf.basic.DefaultMenuLayout; import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.SystemInfo; +import com.formdev.flatlaf.util.UIScale; /** * Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}. @@ -100,6 +105,10 @@ public class FlatMenuBarUI super.installDefaults(); LookAndFeel.installProperty( menuBar, "opaque", false ); + + LayoutManager layout = menuBar.getLayout(); + if( layout == null || layout instanceof UIResource ) + menuBar.setLayout( new FlatMenuBarLayout( menuBar ) ); } @Override @@ -221,6 +230,130 @@ public class FlatMenuBarUI rootPane.getWindowDecorationStyle() != JRootPane.NONE; } + //---- class FlatMenuBarLayout -------------------------------------------- + + /** + * @since 2.4 + */ + protected static class FlatMenuBarLayout + extends DefaultMenuLayout + { + public FlatMenuBarLayout( Container target ) { + super( target, BoxLayout.LINE_AXIS ); + } + + @Override + public void layoutContainer( Container target ) { + super.layoutContainer( target ); + + + // The only purpose of the code below is to make sure that a horizontal glue, + // which can be used to move window and displays the window title in embedded menu bar, + // is always visible within the menu bar bounds and has a minimum width. + // If this is not the case, the horizontal glue is made larger and + // components that are on the left side of the glue are made smaller. + + + // get root pane and check whether this menu bar is the root pane menu bar + JRootPane rootPane = SwingUtilities.getRootPane( target ); + if( rootPane == null || rootPane.getJMenuBar() != target ) + return; + + // get title pane and check whether menu bar is embedded + FlatTitlePane titlePane = FlatRootPaneUI.getTitlePane( rootPane ); + if( titlePane == null || !titlePane.isMenuBarEmbedded() ) + return; + + // check whether there is a horizontal glue (used for window title in embedded menu bar) + // and check minimum width of horizontal glue + Component horizontalGlue = titlePane.findHorizontalGlue( (JMenuBar) target ); + int minTitleWidth = UIScale.scale( titlePane.titleMinimumWidth ); + if( horizontalGlue != null && horizontalGlue.getWidth() < minTitleWidth ) { + // get index of glue component + int glueIndex = -1; + Component[] components = target.getComponents(); + for( int i = components.length - 1; i >= 0; i-- ) { + if( components[i] == horizontalGlue ) { + glueIndex = i; + break; + } + } + if( glueIndex < 0 ) + return; // should never happen + + if( target.getComponentOrientation().isLeftToRight() ) { + // left-to-right + + // make horizontal glue wider (minimum title width) + int offset = minTitleWidth - horizontalGlue.getWidth(); + horizontalGlue.setSize( minTitleWidth, horizontalGlue.getHeight() ); + + // check whether glue is fully visible + int minGlueX = target.getWidth() - target.getInsets().right - minTitleWidth; + if( minGlueX < horizontalGlue.getX() ) { + // move glue to the left to make it fully visible + offset -= (horizontalGlue.getX() - minGlueX); + horizontalGlue.setLocation( minGlueX, horizontalGlue.getY() ); + + // shrink and move components that are on the left side of the glue + for( int i = glueIndex - 1; i >= 0; i-- ) { + Component c = components[i]; + if( c.getX() > minGlueX ) { + // move component and set width to zero + c.setBounds( minGlueX, c.getY(), 0, c.getHeight() ); + } else { + // reduce size of component + c.setSize( minGlueX - c.getX(), c.getHeight() ); + break; + } + } + } + + // move components that are on the right side of the glue + for( int i = glueIndex + 1; i < components.length; i++ ) { + Component c = components[i]; + c.setLocation( c.getX() + offset, c.getY() ); + } + } else { + // right-to-left + + // make horizontal glue wider (minimum title width) + int offset = minTitleWidth - horizontalGlue.getWidth(); + horizontalGlue.setBounds( horizontalGlue.getX() - offset, horizontalGlue.getY(), + minTitleWidth, horizontalGlue.getHeight() ); + + // check whether glue is fully visible + int minGlueX = target.getInsets().left; + if( minGlueX > horizontalGlue.getX() ) { + // move glue to the right to make it fully visible + offset -= (horizontalGlue.getX() - minGlueX); + horizontalGlue.setLocation( minGlueX, horizontalGlue.getY() ); + + // shrink and move components that are on the right side of the glue + int x = horizontalGlue.getX() + horizontalGlue.getWidth(); + for( int i = glueIndex - 1; i >= 0; i-- ) { + Component c = components[i]; + if( c.getX() + c.getWidth() < x ) { + // move component and set width to zero + c.setBounds( x, c.getY(), 0, c.getHeight() ); + } else { + // move component and reduce size + c.setBounds( x, c.getY(), c.getWidth() - (x - c.getX()), c.getHeight() ); + break; + } + } + } + + // move components that are on the left side of the glue + for( int i = glueIndex + 1; i < components.length; i++ ) { + Component c = components[i]; + c.setLocation( c.getX() - offset, c.getY() ); + } + } + } + } + } + //---- class TakeFocus ---------------------------------------------------- /** diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupMenuUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupMenuUI.java index 3c0014ab..2ea093a5 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupMenuUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupMenuUI.java @@ -130,7 +130,7 @@ public class FlatPopupMenuUI LayoutManager layout = popupMenu.getLayout(); if( layout == null || layout instanceof UIResource ) - popupMenu.setLayout( new FlatMenuLayout( popupMenu, BoxLayout.Y_AXIS ) ); + popupMenu.setLayout( new FlatPopupMenuLayout( popupMenu, BoxLayout.Y_AXIS ) ); } @Override @@ -230,12 +230,15 @@ public class FlatPopupMenuUI return screenBounds.height - screenInsets.top - screenInsets.bottom; } - //---- class FlatMenuLayout ----------------------------------------------- + //---- class FlatPopupMenuLayout ------------------------------------------ - protected static class FlatMenuLayout + /** + * @since 2.4 + */ + protected static class FlatPopupMenuLayout extends DefaultMenuLayout { - public FlatMenuLayout( Container target, int axis ) { + public FlatPopupMenuLayout( Container target, int axis ) { super( target, axis ); } 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 eee1b3c7..54640afd 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 @@ -364,6 +364,12 @@ public class FlatRootPaneUI ((FlatRootPaneUI)ui).titlePane.isMenuBarEmbedded(); } + /** @since 2.4 */ + protected static FlatTitlePane getTitlePane( JRootPane rootPane ) { + RootPaneUI ui = rootPane.getUI(); + return ui instanceof FlatRootPaneUI ? ((FlatRootPaneUI)ui).titlePane : null; + } + //---- class FlatRootLayout ----------------------------------------------- protected class FlatRootLayout 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 c8f99783..c7e2937f 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 @@ -241,6 +241,23 @@ public class FlatWindowDecorationsTest if( c instanceof Box.Filler ) return; } + +/*debug + menuBar.add( new Box.Filler( new Dimension(), new Dimension(), + new Dimension( Short.MAX_VALUE, Short.MAX_VALUE ) ) + { + @Override + protected void paintComponent( Graphics g ) { + int w = getWidth(); + int h = getHeight(); + g.setColor( Color.blue ); + g.drawRect( 0, 0, w - 1, h - 1 ); + g.drawLine( 0, 0, w, h ); + g.drawLine( 0, h, w, 0 ); + } + } ); +debug*/ + menuBar.add( Box.createGlue() ); menuBar.revalidate(); }