diff --git a/CHANGELOG.md b/CHANGELOG.md index fc2e533d..eece0303 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ FlatLaf Change Log #### Fixed bugs +- macOS: Setting window background (of undecorated window) to translucent color + (alpha < 255) did not show the window translucent. (issue #705) - JIDE CommandMenuBar: Fixed `ClassCastException` when JIDE command bar displays `JideMenu` in popup. (PR #794) 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 65960fa2..2edef9e9 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 @@ -30,6 +30,7 @@ import java.awt.LayoutManager2; import java.awt.Window; import java.awt.event.ComponentListener; import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.util.function.Function; import javax.swing.JComponent; import javax.swing.JDialog; @@ -85,6 +86,7 @@ public class FlatRootPaneUI private Object nativeWindowBorderData; private LayoutManager oldLayout; private ComponentListener macFullWindowContentListener; + private PropertyChangeListener macWindowBackgroundListener; public static ComponentUI createUI( JComponent c ) { return new FlatRootPaneUI(); @@ -153,6 +155,8 @@ public class FlatRootPaneUI if( background == null || background instanceof UIResource ) parent.setBackground( UIManager.getColor( "control" ) ); } + + macClearBackgroundForTranslucentWindow( c ); } @Override @@ -174,6 +178,7 @@ public class FlatRootPaneUI if( SystemInfo.isMacFullWindowContentSupported ) macFullWindowContentListener = FullWindowContentSupport.macInstallListeners( root ); + macInstallWindowBackgroundListener( root ); } @Override @@ -184,6 +189,7 @@ public class FlatRootPaneUI FullWindowContentSupport.macUninstallListeners( root, macFullWindowContentListener ); macFullWindowContentListener = null; } + macUninstallWindowBackgroundListener( root ); } /** @since 1.1.2 */ @@ -296,7 +302,7 @@ public class FlatRootPaneUI } } - FlatNativeMacLibrary.setWindowButtonsSpacing( getParentWindow(), buttonsSpacing ); + FlatNativeMacLibrary.setWindowButtonsSpacing( getParentWindow( rootPane ), buttonsSpacing ); } // update buttons bounds client property @@ -313,7 +319,7 @@ public class FlatRootPaneUI // reset window buttons spacing if( isMacButtonsSpacingSupported() ) - FlatNativeMacLibrary.setWindowButtonsSpacing( getParentWindow(), FlatNativeMacLibrary.BUTTONS_SPACING_DEFAULT ); + FlatNativeMacLibrary.setWindowButtonsSpacing( getParentWindow( rootPane ), FlatNativeMacLibrary.BUTTONS_SPACING_DEFAULT ); // remove buttons bounds client property FullWindowContentSupport.macUninstallFullWindowContentButtonsBoundsProperty( rootPane ); @@ -323,11 +329,60 @@ public class FlatRootPaneUI return SystemInfo.isMacOS && SystemInfo.isJava_17_orLater && FlatNativeMacLibrary.isLoaded(); } - private Window getParentWindow() { + private void macInstallWindowBackgroundListener( JRootPane c ) { + if( !SystemInfo.isMacOS ) + return; + + Window window = getParentWindow( c ); + if( window != null && macWindowBackgroundListener == null ) { + macWindowBackgroundListener = e -> macClearBackgroundForTranslucentWindow( c ); + window.addPropertyChangeListener( "background", macWindowBackgroundListener ); + } + } + + private void macUninstallWindowBackgroundListener( JRootPane c ) { + if( !SystemInfo.isMacOS ) + return; + + Window window = getParentWindow( c ); + if( window != null && macWindowBackgroundListener != null ) { + window.removePropertyChangeListener( "background", macWindowBackgroundListener ); + macWindowBackgroundListener = null; + } + } + + /** + * When setting window background to translucent color (alpha < 255), + * Swing paints that window translucent on Windows and Linux, but not on macOS. + * The reason for this is that FlatLaf sets the background color of the root pane, + * and Swing behaves a bit differently on macOS than on other platforms in that case. + * Other L&Fs do not set root pane background, which is {@code null} by default. + *
+ * To fix this problem, set the root pane background to {@code null} + * if windows uses a translucent background. + */ + private void macClearBackgroundForTranslucentWindow( JRootPane c ) { + if( !SystemInfo.isMacOS ) + return; + + Window window = getParentWindow( c ); + if( window != null ) { + Color windowBackground = window.getBackground(); + if( windowBackground != null && + windowBackground.getAlpha() < 255 && + c.getBackground() instanceof UIResource ) + { + // clear root pane background + c.setBackground( null ); + } + } + } + + private Window getParentWindow( JRootPane c ) { // not using SwingUtilities.windowForComponent() or SwingUtilities.getWindowAncestor() // here because root panes may be nested and used anywhere (e.g. in JInternalFrame) // but we're only interested in the "root" root pane, which is a direct child of the window - Container parent = rootPane.getParent(); + Container parent = c.getParent(); return (parent instanceof Window) ? (Window) parent : null; } @@ -389,6 +444,12 @@ public class FlatRootPaneUI break; case "ancestor": + if( e.getNewValue() instanceof Window ) + macClearBackgroundForTranslucentWindow( rootPane ); + + macUninstallWindowBackgroundListener( rootPane ); + macInstallWindowBackgroundListener( rootPane ); + // FlatNativeMacLibrary.setWindowButtonsSpacing() and // FullWindowContentSupport.macUpdateFullWindowContentButtonsBoundsProperty() // require a native window, but setting the client properties diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatTestPanel.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatTestPanel.java index f35ff406..50950b85 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatTestPanel.java +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatTestPanel.java @@ -32,8 +32,10 @@ public class FlatTestPanel int width = getWidth(); int height = getHeight(); - g.setColor( super.getBackground() ); - g.fillRect( 0, 0, width, height ); + if( isOpaque() ) { + g.setColor( super.getBackground() ); + g.fillRect( 0, 0, width, height ); + } if( isPaintBackgroundPattern() ) { g.setColor( Color.magenta ); 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 efacf6fc..85a62fab 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 @@ -76,6 +76,14 @@ public class FlatWindowDecorationsTest initComponents(); } + @Override + public void updateUI() { + super.updateUI(); + + if( translucentWindowBackgroundCheckBox != null ) + translucentWindowBackgroundChanged(); + } + @Override public void addNotify() { super.addNotify(); @@ -237,6 +245,27 @@ public class FlatWindowDecorationsTest } } + private void translucentWindowBackgroundChanged() { + boolean selected = translucentWindowBackgroundCheckBox.isSelected(); + if( selected && !undecoratedCheckBox.isSelected() ) { + undecoratedCheckBox.setSelected( true ); + undecoratedChanged(); + } + undecoratedCheckBox.setEnabled( !selected ); + + Color background = selected + ? new Color( 100, 0, 0, 100 ) + : UIManager.getColor( "control" ); + + Window window = SwingUtilities.windowForComponent( this ); + window.setBackground( background ); + + for( Component c = this; c != null; c = c.getParent() ) { + if( c instanceof JComponent ) + LookAndFeel.installProperty( (JComponent) c, "opaque", !selected ); + } + } + private void addMenu() { JMenu menu = new JMenu( "Hello" ); menu.add( new JMenuItem( "world" ) ); @@ -543,6 +572,7 @@ debug*/ colorizeTitleBarCheckBox = new JCheckBox(); colorizeMenuBarCheckBox = new JCheckBox(); colorizeMenusCheckBox = new JCheckBox(); + translucentWindowBackgroundCheckBox = new JCheckBox(); JButton openDialogButton = new JButton(); JButton openFrameButton = new JButton(); typeNormalRadioButton = new JRadioButton(); @@ -885,6 +915,7 @@ debug*/ "[]" + "[]" + "[]" + + "[]para" + "[]")); //---- unifiedBackgroundCheckBox ---- @@ -906,6 +937,11 @@ debug*/ colorizeMenusCheckBox.setText("colorize menus"); colorizeMenusCheckBox.addActionListener(e -> colorizeMenus()); panel5.add(colorizeMenusCheckBox, "cell 0 3"); + + //---- translucentWindowBackgroundCheckBox ---- + translucentWindowBackgroundCheckBox.setText("translucent window background"); + translucentWindowBackgroundCheckBox.addActionListener(e -> translucentWindowBackgroundChanged()); + panel5.add(translucentWindowBackgroundCheckBox, "cell 0 4"); } add(panel5, "cell 2 1"); @@ -1169,6 +1205,7 @@ debug*/ private JCheckBox colorizeTitleBarCheckBox; private JCheckBox colorizeMenuBarCheckBox; private JCheckBox colorizeMenusCheckBox; + private JCheckBox translucentWindowBackgroundCheckBox; private JRadioButton typeNormalRadioButton; private JRadioButton typeUtilityRadioButton; private JRadioButton typeSmallRadioButton; 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 c7c40684..484c1fac 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 @@ -1,4 +1,4 @@ -JFDML JFormDesigner: "8.1.0.0.283" Java: "19.0.2" encoding: "UTF-8" +JFDML JFormDesigner: "8.2.1.0.348" Java: "21.0.1" encoding: "UTF-8" new FormModel { contentType: "form/swing" @@ -449,7 +449,7 @@ new FormModel { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { "$layoutConstraints": "ltr,hidemode 3,gap 0 0" "$columnConstraints": "[left]" - "$rowConstraints": "[][][][]" + "$rowConstraints": "[][][][]para[]" } ) { name: "panel5" "border": new javax.swing.border.TitledBorder( "Color" ) @@ -493,6 +493,16 @@ new FormModel { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 0 3" } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "translucentWindowBackgroundCheckBox" + "text": "translucent window background" + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "translucentWindowBackgroundChanged", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 4" + } ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 2 1" } )