macOS: setting window background (of undecorated window) to translucent color (alpha < 255) did not show the window translucent (issue #705)

This commit is contained in:
Karl Tauber
2024-01-28 11:57:17 +01:00
parent 16ddd100d3
commit 30af74f806
5 changed files with 120 additions and 8 deletions

View File

@@ -16,6 +16,8 @@ FlatLaf Change Log
#### Fixed bugs #### 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 - JIDE CommandMenuBar: Fixed `ClassCastException` when JIDE command bar displays
`JideMenu` in popup. (PR #794) `JideMenu` in popup. (PR #794)

View File

@@ -30,6 +30,7 @@ import java.awt.LayoutManager2;
import java.awt.Window; import java.awt.Window;
import java.awt.event.ComponentListener; import java.awt.event.ComponentListener;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.function.Function; import java.util.function.Function;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JDialog; import javax.swing.JDialog;
@@ -85,6 +86,7 @@ public class FlatRootPaneUI
private Object nativeWindowBorderData; private Object nativeWindowBorderData;
private LayoutManager oldLayout; private LayoutManager oldLayout;
private ComponentListener macFullWindowContentListener; private ComponentListener macFullWindowContentListener;
private PropertyChangeListener macWindowBackgroundListener;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatRootPaneUI(); return new FlatRootPaneUI();
@@ -153,6 +155,8 @@ public class FlatRootPaneUI
if( background == null || background instanceof UIResource ) if( background == null || background instanceof UIResource )
parent.setBackground( UIManager.getColor( "control" ) ); parent.setBackground( UIManager.getColor( "control" ) );
} }
macClearBackgroundForTranslucentWindow( c );
} }
@Override @Override
@@ -174,6 +178,7 @@ public class FlatRootPaneUI
if( SystemInfo.isMacFullWindowContentSupported ) if( SystemInfo.isMacFullWindowContentSupported )
macFullWindowContentListener = FullWindowContentSupport.macInstallListeners( root ); macFullWindowContentListener = FullWindowContentSupport.macInstallListeners( root );
macInstallWindowBackgroundListener( root );
} }
@Override @Override
@@ -184,6 +189,7 @@ public class FlatRootPaneUI
FullWindowContentSupport.macUninstallListeners( root, macFullWindowContentListener ); FullWindowContentSupport.macUninstallListeners( root, macFullWindowContentListener );
macFullWindowContentListener = null; macFullWindowContentListener = null;
} }
macUninstallWindowBackgroundListener( root );
} }
/** @since 1.1.2 */ /** @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 // update buttons bounds client property
@@ -313,7 +319,7 @@ public class FlatRootPaneUI
// reset window buttons spacing // reset window buttons spacing
if( isMacButtonsSpacingSupported() ) if( isMacButtonsSpacingSupported() )
FlatNativeMacLibrary.setWindowButtonsSpacing( getParentWindow(), FlatNativeMacLibrary.BUTTONS_SPACING_DEFAULT ); FlatNativeMacLibrary.setWindowButtonsSpacing( getParentWindow( rootPane ), FlatNativeMacLibrary.BUTTONS_SPACING_DEFAULT );
// remove buttons bounds client property // remove buttons bounds client property
FullWindowContentSupport.macUninstallFullWindowContentButtonsBoundsProperty( rootPane ); FullWindowContentSupport.macUninstallFullWindowContentButtonsBoundsProperty( rootPane );
@@ -323,11 +329,60 @@ public class FlatRootPaneUI
return SystemInfo.isMacOS && SystemInfo.isJava_17_orLater && FlatNativeMacLibrary.isLoaded(); 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.
* <p>
* 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() // not using SwingUtilities.windowForComponent() or SwingUtilities.getWindowAncestor()
// here because root panes may be nested and used anywhere (e.g. in JInternalFrame) // 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 // 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; return (parent instanceof Window) ? (Window) parent : null;
} }
@@ -389,6 +444,12 @@ public class FlatRootPaneUI
break; break;
case "ancestor": case "ancestor":
if( e.getNewValue() instanceof Window )
macClearBackgroundForTranslucentWindow( rootPane );
macUninstallWindowBackgroundListener( rootPane );
macInstallWindowBackgroundListener( rootPane );
// FlatNativeMacLibrary.setWindowButtonsSpacing() and // FlatNativeMacLibrary.setWindowButtonsSpacing() and
// FullWindowContentSupport.macUpdateFullWindowContentButtonsBoundsProperty() // FullWindowContentSupport.macUpdateFullWindowContentButtonsBoundsProperty()
// require a native window, but setting the client properties // require a native window, but setting the client properties

View File

@@ -32,8 +32,10 @@ public class FlatTestPanel
int width = getWidth(); int width = getWidth();
int height = getHeight(); int height = getHeight();
if( isOpaque() ) {
g.setColor( super.getBackground() ); g.setColor( super.getBackground() );
g.fillRect( 0, 0, width, height ); g.fillRect( 0, 0, width, height );
}
if( isPaintBackgroundPattern() ) { if( isPaintBackgroundPattern() ) {
g.setColor( Color.magenta ); g.setColor( Color.magenta );

View File

@@ -76,6 +76,14 @@ public class FlatWindowDecorationsTest
initComponents(); initComponents();
} }
@Override
public void updateUI() {
super.updateUI();
if( translucentWindowBackgroundCheckBox != null )
translucentWindowBackgroundChanged();
}
@Override @Override
public void addNotify() { public void addNotify() {
super.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() { private void addMenu() {
JMenu menu = new JMenu( "Hello" ); JMenu menu = new JMenu( "Hello" );
menu.add( new JMenuItem( "world" ) ); menu.add( new JMenuItem( "world" ) );
@@ -543,6 +572,7 @@ debug*/
colorizeTitleBarCheckBox = new JCheckBox(); colorizeTitleBarCheckBox = new JCheckBox();
colorizeMenuBarCheckBox = new JCheckBox(); colorizeMenuBarCheckBox = new JCheckBox();
colorizeMenusCheckBox = new JCheckBox(); colorizeMenusCheckBox = new JCheckBox();
translucentWindowBackgroundCheckBox = new JCheckBox();
JButton openDialogButton = new JButton(); JButton openDialogButton = new JButton();
JButton openFrameButton = new JButton(); JButton openFrameButton = new JButton();
typeNormalRadioButton = new JRadioButton(); typeNormalRadioButton = new JRadioButton();
@@ -885,6 +915,7 @@ debug*/
"[]" + "[]" +
"[]" + "[]" +
"[]" + "[]" +
"[]para" +
"[]")); "[]"));
//---- unifiedBackgroundCheckBox ---- //---- unifiedBackgroundCheckBox ----
@@ -906,6 +937,11 @@ debug*/
colorizeMenusCheckBox.setText("colorize menus"); colorizeMenusCheckBox.setText("colorize menus");
colorizeMenusCheckBox.addActionListener(e -> colorizeMenus()); colorizeMenusCheckBox.addActionListener(e -> colorizeMenus());
panel5.add(colorizeMenusCheckBox, "cell 0 3"); 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"); add(panel5, "cell 2 1");
@@ -1169,6 +1205,7 @@ debug*/
private JCheckBox colorizeTitleBarCheckBox; private JCheckBox colorizeTitleBarCheckBox;
private JCheckBox colorizeMenuBarCheckBox; private JCheckBox colorizeMenuBarCheckBox;
private JCheckBox colorizeMenusCheckBox; private JCheckBox colorizeMenusCheckBox;
private JCheckBox translucentWindowBackgroundCheckBox;
private JRadioButton typeNormalRadioButton; private JRadioButton typeNormalRadioButton;
private JRadioButton typeUtilityRadioButton; private JRadioButton typeUtilityRadioButton;
private JRadioButton typeSmallRadioButton; private JRadioButton typeSmallRadioButton;

View File

@@ -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 { new FormModel {
contentType: "form/swing" contentType: "form/swing"
@@ -449,7 +449,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,hidemode 3,gap 0 0" "$layoutConstraints": "ltr,hidemode 3,gap 0 0"
"$columnConstraints": "[left]" "$columnConstraints": "[left]"
"$rowConstraints": "[][][][]" "$rowConstraints": "[][][][]para[]"
} ) { } ) {
name: "panel5" name: "panel5"
"border": new javax.swing.border.TitledBorder( "Color" ) "border": new javax.swing.border.TitledBorder( "Color" )
@@ -493,6 +493,16 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3" "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 ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 1" "value": "cell 2 1"
} ) } )