mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-12 23:07:15 -06:00
Window decorations: support embedding menu bar into title pane (enabled by default) (issues #47 and #82)
This commit is contained in:
@@ -175,6 +175,15 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String PROGRESS_BAR_SQUARE = "JProgressBar.square";
|
||||
|
||||
/**
|
||||
* Specifies whether the menu bar is embedded into the title pane if custom
|
||||
* window decorations are enabled. Default is {@code true}.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
*/
|
||||
String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded";
|
||||
|
||||
/**
|
||||
* Specifies whether the decrease/increase arrow buttons of a scrollbar are shown.
|
||||
* <p>
|
||||
|
||||
@@ -30,6 +30,7 @@ import javax.swing.JMenuBar;
|
||||
import javax.swing.JRootPane;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicRootPaneUI;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.util.SystemInfo;
|
||||
|
||||
/**
|
||||
@@ -41,7 +42,7 @@ public class FlatRootPaneUI
|
||||
extends BasicRootPaneUI
|
||||
{
|
||||
private JRootPane rootPane;
|
||||
private JComponent titlePane;
|
||||
private FlatTitlePane titlePane;
|
||||
private LayoutManager oldLayout;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
@@ -92,14 +93,17 @@ public class FlatRootPaneUI
|
||||
}
|
||||
}
|
||||
|
||||
private void setTitlePane( JComponent newTitlePane ) {
|
||||
// layer title pane under frame content layer to allow placing menu bar over title pane
|
||||
private final static Integer TITLE_PANE_LAYER = JLayeredPane.FRAME_CONTENT_LAYER - 1;
|
||||
|
||||
private void setTitlePane( FlatTitlePane newTitlePane ) {
|
||||
JLayeredPane layeredPane = rootPane.getLayeredPane();
|
||||
|
||||
if( titlePane != null )
|
||||
layeredPane.remove( titlePane );
|
||||
|
||||
if( newTitlePane != null )
|
||||
layeredPane.add( newTitlePane, JLayeredPane.FRAME_CONTENT_LAYER );
|
||||
layeredPane.add( newTitlePane, TITLE_PANE_LAYER );
|
||||
|
||||
titlePane = newTitlePane;
|
||||
}
|
||||
@@ -114,6 +118,14 @@ public class FlatRootPaneUI
|
||||
if( rootPane.getWindowDecorationStyle() != JRootPane.NONE )
|
||||
installClientDecorations();
|
||||
break;
|
||||
|
||||
case FlatClientProperties.MENU_BAR_EMBEDDED:
|
||||
if( titlePane != null ) {
|
||||
titlePane.menuBarChanged();
|
||||
rootPane.revalidate();
|
||||
rootPane.repaint();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +137,6 @@ public class FlatRootPaneUI
|
||||
@Override public void addLayoutComponent( String name, Component comp ) {}
|
||||
@Override public void addLayoutComponent( Component comp, Object constraints ) {}
|
||||
@Override public void removeLayoutComponent( Component comp ) {}
|
||||
@Override public void invalidateLayout( Container target ) {}
|
||||
|
||||
@Override
|
||||
public Dimension preferredLayoutSize( Container parent ) {
|
||||
@@ -144,20 +155,26 @@ public class FlatRootPaneUI
|
||||
|
||||
private Dimension computeLayoutSize( Container parent, Function<Component, Dimension> getSizeFunc ) {
|
||||
JRootPane rootPane = (JRootPane) parent;
|
||||
JComponent titlePane = getTitlePane( rootPane );
|
||||
FlatTitlePane titlePane = getTitlePane( rootPane );
|
||||
|
||||
Dimension titlePaneSize = (titlePane != null)
|
||||
? getSizeFunc.apply( titlePane )
|
||||
: new Dimension();
|
||||
Dimension menuBarSize = (rootPane.getJMenuBar() != null)
|
||||
? getSizeFunc.apply( rootPane.getJMenuBar() )
|
||||
: new Dimension();
|
||||
Dimension contentSize = (rootPane.getContentPane() != null)
|
||||
? getSizeFunc.apply( rootPane.getContentPane() )
|
||||
: rootPane.getSize();
|
||||
|
||||
int width = Math.max( titlePaneSize.width, Math.max( menuBarSize.width, contentSize.width ) );
|
||||
int height = titlePaneSize.height + menuBarSize.height + contentSize.height;
|
||||
int width = Math.max( titlePaneSize.width, contentSize.width );
|
||||
int height = titlePaneSize.height + contentSize.height;
|
||||
if( titlePane == null || !titlePane.isMenuBarEmbedded() ) {
|
||||
Dimension menuBarSize = (rootPane.getJMenuBar() != null)
|
||||
? getSizeFunc.apply( rootPane.getJMenuBar() )
|
||||
: new Dimension();
|
||||
|
||||
width = Math.max( width, menuBarSize.width );
|
||||
height += menuBarSize.height;
|
||||
}
|
||||
|
||||
Insets insets = rootPane.getInsets();
|
||||
|
||||
return new Dimension(
|
||||
@@ -165,7 +182,7 @@ public class FlatRootPaneUI
|
||||
height + insets.top + insets.bottom );
|
||||
}
|
||||
|
||||
private JComponent getTitlePane( JRootPane rootPane ) {
|
||||
private FlatTitlePane getTitlePane( JRootPane rootPane ) {
|
||||
return (rootPane.getWindowDecorationStyle() != JRootPane.NONE &&
|
||||
rootPane.getUI() instanceof FlatRootPaneUI)
|
||||
? ((FlatRootPaneUI)rootPane.getUI()).titlePane
|
||||
@@ -188,7 +205,7 @@ public class FlatRootPaneUI
|
||||
rootPane.getGlassPane().setBounds( x, y, width, height );
|
||||
|
||||
int nextY = 0;
|
||||
JComponent titlePane = getTitlePane( rootPane );
|
||||
FlatTitlePane titlePane = getTitlePane( rootPane );
|
||||
if( titlePane != null ) {
|
||||
Dimension prefSize = titlePane.getPreferredSize();
|
||||
titlePane.setBounds( 0, 0, width, prefSize.height );
|
||||
@@ -197,9 +214,14 @@ public class FlatRootPaneUI
|
||||
|
||||
JMenuBar menuBar = rootPane.getJMenuBar();
|
||||
if( menuBar != null ) {
|
||||
Dimension prefSize = menuBar.getPreferredSize();
|
||||
menuBar.setBounds( 0, nextY, width, prefSize.height );
|
||||
nextY += prefSize.height;
|
||||
if( titlePane != null && titlePane.isMenuBarEmbedded() ) {
|
||||
titlePane.validate();
|
||||
menuBar.setBounds( titlePane.getMenuBarBounds() );
|
||||
} else {
|
||||
Dimension prefSize = menuBar.getPreferredSize();
|
||||
menuBar.setBounds( 0, nextY, width, prefSize.height );
|
||||
nextY += prefSize.height;
|
||||
}
|
||||
}
|
||||
|
||||
Container contentPane = rootPane.getContentPane();
|
||||
@@ -207,6 +229,13 @@ public class FlatRootPaneUI
|
||||
contentPane.setBounds( 0, nextY, width, Math.max( height - nextY, 0 ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateLayout( Container parent ) {
|
||||
FlatTitlePane titlePane = getTitlePane( (JRootPane) parent );
|
||||
if( titlePane != null && titlePane.isMenuBarEmbedded() )
|
||||
titlePane.menuBarChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getLayoutAlignmentX( Container target ) {
|
||||
return 0;
|
||||
|
||||
@@ -48,10 +48,12 @@ import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JRootPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
@@ -61,9 +63,12 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
* @uiDefault TitlePane.inactiveBackground Color
|
||||
* @uiDefault TitlePane.foreground Color
|
||||
* @uiDefault TitlePane.inactiveForeground Color
|
||||
* @uiDefault TitlePane.embeddedForeground Color
|
||||
* @uiDefault TitlePane.iconSize Dimension
|
||||
* @uiDefault TitlePane.iconMargins Insets
|
||||
* @uiDefault TitlePane.titleMargins Insets
|
||||
* @uiDefault TitlePane.menuBarMargins Insets
|
||||
* @uiDefault TitlePane.menuBarEmbedded boolean
|
||||
* @uiDefault TitlePane.buttonMaximizedHeight int
|
||||
* @uiDefault TitlePane.closeIcon Icon
|
||||
* @uiDefault TitlePane.iconifyIcon Icon
|
||||
@@ -79,13 +84,18 @@ class FlatTitlePane
|
||||
private final Color inactiveBackground = UIManager.getColor( "TitlePane.inactiveBackground" );
|
||||
private final Color activeForeground = UIManager.getColor( "TitlePane.foreground" );
|
||||
private final Color inactiveForeground = UIManager.getColor( "TitlePane.inactiveForeground" );
|
||||
private final Color embeddedForeground = UIManager.getColor( "TitlePane.embeddedForeground" );
|
||||
|
||||
private final boolean menuBarEmbedded = UIManager.getBoolean( "TitlePane.menuBarEmbedded" );
|
||||
private final Insets menuBarMargins = UIManager.getInsets( "TitlePane.menuBarMargins" );
|
||||
private final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" );
|
||||
private final int buttonMaximizedHeight = UIManager.getInt( "TitlePane.buttonMaximizedHeight" );
|
||||
|
||||
private final JRootPane rootPane;
|
||||
|
||||
private JPanel leftPanel;
|
||||
private JLabel iconLabel;
|
||||
private JComponent menuBarPlaceholder;
|
||||
private JLabel titleLabel;
|
||||
private JPanel buttonPanel;
|
||||
private JButton iconifyButton;
|
||||
@@ -107,15 +117,32 @@ class FlatTitlePane
|
||||
}
|
||||
|
||||
private void addSubComponents() {
|
||||
leftPanel = new JPanel();
|
||||
iconLabel = new JLabel();
|
||||
titleLabel = new JLabel();
|
||||
iconLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.iconMargins" ) ) );
|
||||
titleLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.titleMargins" ) ) );
|
||||
|
||||
leftPanel.setBorder( new LineBorder( Color.red ) );
|
||||
leftPanel.setLayout( new BoxLayout( leftPanel, BoxLayout.LINE_AXIS ) );
|
||||
leftPanel.setOpaque( false );
|
||||
leftPanel.add( iconLabel );
|
||||
|
||||
menuBarPlaceholder = new JComponent() {
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
JMenuBar menuBar = rootPane.getJMenuBar();
|
||||
return (menuBar != null && isMenuBarEmbedded())
|
||||
? FlatUIUtils.addInsets( menuBar.getPreferredSize(), UIScale.scale( menuBarMargins ) )
|
||||
: new Dimension();
|
||||
}
|
||||
};
|
||||
leftPanel.add( menuBarPlaceholder );
|
||||
|
||||
createButtons();
|
||||
|
||||
setLayout( new BorderLayout() );
|
||||
add( iconLabel, BorderLayout.LINE_START );
|
||||
add( leftPanel, BorderLayout.LINE_START );
|
||||
add( titleLabel, BorderLayout.CENTER );
|
||||
add( buttonPanel, BorderLayout.LINE_END );
|
||||
}
|
||||
@@ -169,7 +196,9 @@ class FlatTitlePane
|
||||
|
||||
private void activeChanged( boolean active ) {
|
||||
Color background = FlatUIUtils.nonUIResource( active ? activeBackground : inactiveBackground );
|
||||
Color foreground = FlatUIUtils.nonUIResource( active ? activeForeground : inactiveForeground );
|
||||
Color foreground = FlatUIUtils.nonUIResource( active
|
||||
? (rootPane.getJMenuBar() != null && isMenuBarEmbedded() ? embeddedForeground : activeForeground)
|
||||
: inactiveForeground );
|
||||
|
||||
setBackground( background );
|
||||
titleLabel.setForeground( foreground );
|
||||
@@ -278,6 +307,32 @@ class FlatTitlePane
|
||||
window.removeComponentListener( handler );
|
||||
}
|
||||
|
||||
boolean isMenuBarEmbedded() {
|
||||
return menuBarEmbedded && FlatClientProperties.clientPropertyBoolean(
|
||||
rootPane, FlatClientProperties.MENU_BAR_EMBEDDED, true );
|
||||
}
|
||||
|
||||
Rectangle getMenuBarBounds() {
|
||||
Rectangle bounds = menuBarPlaceholder.getBounds();
|
||||
bounds = SwingUtilities.convertRectangle( menuBarPlaceholder.getParent(), bounds, rootPane );
|
||||
return FlatUIUtils.subtractInsets( bounds, UIScale.scale( getMenuBarMargins() ) );
|
||||
}
|
||||
|
||||
void menuBarChanged() {
|
||||
menuBarPlaceholder.invalidate();
|
||||
|
||||
// update title foreground color
|
||||
EventQueue.invokeLater( () -> {
|
||||
activeChanged( window == null || window.isActive() );
|
||||
} );
|
||||
}
|
||||
|
||||
private Insets getMenuBarMargins() {
|
||||
return getComponentOrientation().isLeftToRight()
|
||||
? menuBarMargins
|
||||
: new Insets( menuBarMargins.top, menuBarMargins.right, menuBarMargins.bottom, menuBarMargins.left );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent( Graphics g ) {
|
||||
g.setColor( getBackground() );
|
||||
@@ -373,7 +428,8 @@ class FlatTitlePane
|
||||
return;
|
||||
|
||||
List<Rectangle> hitTestSpots = new ArrayList<>();
|
||||
addJBRHitTestSpot( buttonPanel, hitTestSpots );
|
||||
addJBRHitTestSpot( buttonPanel, false, hitTestSpots );
|
||||
addJBRHitTestSpot( menuBarPlaceholder, true, hitTestSpots );//TOOD
|
||||
|
||||
int titleBarHeight = getHeight();
|
||||
// slightly reduce height so that component receives mouseExit events
|
||||
@@ -383,13 +439,15 @@ class FlatTitlePane
|
||||
JBRCustomDecorations.setHitTestSpotsAndTitleBarHeight( window, hitTestSpots, titleBarHeight );
|
||||
}
|
||||
|
||||
private void addJBRHitTestSpot( JComponent c, List<Rectangle> hitTestSpots ) {
|
||||
private void addJBRHitTestSpot( JComponent c, boolean subtractMenuBarMargins, List<Rectangle> hitTestSpots ) {
|
||||
Dimension size = c.getSize();
|
||||
if( size.width <= 0 || size.height <= 0 )
|
||||
return;
|
||||
|
||||
Point location = SwingUtilities.convertPoint( c, 0, 0, window );
|
||||
Rectangle r = new Rectangle( location, size );
|
||||
if( subtractMenuBarMargins )
|
||||
r = FlatUIUtils.subtractInsets( r, UIScale.scale( getMenuBarMargins() ) );
|
||||
// slightly increase rectangle so that component receives mouseExit events
|
||||
r.grow( 2, 2 );
|
||||
hitTestSpots.add( r );
|
||||
@@ -506,7 +564,7 @@ class FlatTitlePane
|
||||
|
||||
int restoredWidth = window.getWidth();
|
||||
int newX = maximizedX;
|
||||
JComponent rightComp = getComponentOrientation().isLeftToRight() ? buttonPanel : iconLabel;
|
||||
JComponent rightComp = getComponentOrientation().isLeftToRight() ? buttonPanel : leftPanel;
|
||||
if( xOnScreen >= maximizedX + restoredWidth - rightComp.getWidth() - 10 )
|
||||
newX = xOnScreen + rightComp.getWidth() + 10 - restoredWidth;
|
||||
|
||||
|
||||
@@ -268,6 +268,7 @@ TableHeader.bottomSeparatorColor=$TableHeader.separatorColor
|
||||
|
||||
#---- TitlePane ----
|
||||
|
||||
TitlePane.embeddedForeground=darken($TitlePane.foreground,15%)
|
||||
TitlePane.buttonHoverBackground=lighten($TitlePane.background,10%,derived)
|
||||
TitlePane.buttonPressedBackground=lighten($TitlePane.background,20%,derived)
|
||||
|
||||
|
||||
@@ -576,8 +576,10 @@ TitledBorder.border=1,1,1,1,$Separator.foreground
|
||||
|
||||
#---- TitlePane ----
|
||||
|
||||
TitlePane.menuBarEmbedded=true
|
||||
TitlePane.iconSize=16,16
|
||||
TitlePane.iconMargins=3,8,3,0
|
||||
TitlePane.menuBarMargins=0,8,0,22
|
||||
TitlePane.titleMargins=3,8,3,8
|
||||
TitlePane.buttonSize=44,30
|
||||
TitlePane.buttonMaximizedHeight=22
|
||||
|
||||
@@ -275,6 +275,7 @@ TableHeader.bottomSeparatorColor=$TableHeader.separatorColor
|
||||
|
||||
#---- TitlePane ----
|
||||
|
||||
TitlePane.embeddedForeground=lighten($TitlePane.foreground,35%)
|
||||
TitlePane.buttonHoverBackground=darken($TitlePane.background,10%,derived)
|
||||
TitlePane.buttonPressedBackground=darken($TitlePane.background,20%,derived)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user