diff --git a/CHANGELOG.md b/CHANGELOG.md index 95a308db..87da0078 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ FlatLaf Change Log ## 1.2-SNAPSHOT +#### New features and improvements + +- Native window decorations: Added API to check whether current platform + supports window decorations (`FlatLaf.supportsNativeWindowDecorations()`) and + to toggle window decorations of all windows + (`FlatLaf.setUseNativeWindowDecorations(boolean)`). + #### Fixed bugs - Native window decorations: Fixed loading of native library when using Java diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java index 6a82e2ac..221a069e 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java @@ -233,10 +233,14 @@ public interface FlatClientProperties /** * Specifies whether FlatLaf native window decorations should be used - * when creating {@code JFrame} or {@code JDialog}. + * for {@code JFrame} or {@code JDialog}. *
- * Setting this to {@code false} disables using FlatLaf native window decorations - * for the window that contains the root pane. Needs to be set before showing the window. + * Setting this enables/disables using FlatLaf native window decorations + * for the window that contains the root pane. + *
+ * This client property has lower priority than system property + * {@link FlatSystemProperties#USE_WINDOW_DECORATIONS}, but higher priority + * than UI default {@code TitlePane.useWindowDecorations}. *
* (requires Window 10) *
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java index 41db5a9b..9e865a41 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java @@ -46,6 +46,7 @@ import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.LookAndFeel; import javax.swing.PopupFactory; +import javax.swing.RootPaneContainer; import javax.swing.SwingUtilities; import javax.swing.UIDefaults; import javax.swing.UIDefaults.ActiveValue; @@ -59,6 +60,7 @@ import javax.swing.text.StyleContext; import javax.swing.text.html.HTMLEditorKit; import com.formdev.flatlaf.ui.FlatNativeWindowBorder; import com.formdev.flatlaf.ui.FlatPopupFactory; +import com.formdev.flatlaf.ui.FlatRootPaneUI; import com.formdev.flatlaf.util.GrayFilter; import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.MultiResolutionImageSupport; @@ -715,6 +717,79 @@ public abstract class FlatLaf } ); } + /** + * Returns whether native window decorations are supported on current platform. + *
+ * This requires Windows 10, but may be disabled if running in special environments + * (JetBrains Projector, Webswing or WinPE) or if loading native library fails. + * If system property {@link FlatSystemProperties#USE_WINDOW_DECORATIONS} is set to + * {@code false}, then this method also returns {@code false}. + * + * @since 1.1.2 + */ + public static boolean supportsNativeWindowDecorations() { + return SystemInfo.isWindows_10_orLater && FlatNativeWindowBorder.isSupported(); + } + + /** + * Returns whether native window decorations are enabled. + * + * @since 1.1.2 + */ + public static boolean isUseNativeWindowDecorations() { + return UIManager.getBoolean( "TitlePane.useWindowDecorations" ); + } + + /** + * Sets whether native window decorations are enabled. + *
+ * Existing frames and dialogs will be updated. + * + * @since 1.1.2 + */ + public static void setUseNativeWindowDecorations( boolean enabled ) { + UIManager.put( "TitlePane.useWindowDecorations", enabled ); + + if( !(UIManager.getLookAndFeel() instanceof FlatLaf) ) + return; + + // update existing frames and dialogs + for( Window w : Window.getWindows() ) { + if( isDisplayableFrameOrDialog( w ) ) + FlatRootPaneUI.updateNativeWindowBorder( ((RootPaneContainer)w).getRootPane() ); + } + } + + /** + * Revalidate and repaint all displayable frames and dialogs. + * + * @since 1.1.2 + */ + public static void revalidateAndRepaintAllFramesAndDialogs() { + for( Window w : Window.getWindows() ) { + if( isDisplayableFrameOrDialog( w ) ) { + w.revalidate(); + w.repaint(); + } + } + } + + /** + * Repaint all displayable frames and dialogs. + * + * @since 1.1.2 + */ + public static void repaintAllFramesAndDialogs() { + for( Window w : Window.getWindows() ) { + if( isDisplayableFrameOrDialog( w ) ) + w.repaint(); + } + } + + private static boolean isDisplayableFrameOrDialog( Window w ) { + return w.isDisplayable() && (w instanceof JFrame || w instanceof JDialog); + } + public static boolean isShowMnemonics() { return MnemonicHandler.isShowMnemonics(); } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatSystemProperties.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatSystemProperties.java index 7ec39204..1a1040d5 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatSystemProperties.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatSystemProperties.java @@ -63,6 +63,10 @@ public interface FlatSystemProperties *
* Setting this to {@code false} disables using FlatLaf native window decorations. *
+ * This system property has higher priority than client property + * {@link FlatClientProperties#USE_WINDOW_DECORATIONS} and + * UI default {@code TitlePane.useWindowDecorations}. + *
* (requires Window 10) *
* Allowed Values {@code false} and {@code true}
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeWindowBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeWindowBorder.java
index 1d640898..49a0332f 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeWindowBorder.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeWindowBorder.java
@@ -99,18 +99,12 @@ public class FlatNativeWindowBorder
if( window instanceof JFrame ) {
JFrame frame = (JFrame) window;
+ JRootPane rootPane = frame.getRootPane();
- // check whether disabled via client property
- if( !FlatClientProperties.clientPropertyBoolean( frame.getRootPane(), FlatClientProperties.USE_WINDOW_DECORATIONS, true ) )
+ // check whether disabled via system property, client property or UI default
+ if( !useWindowDecorations( rootPane, systemPropertyKey ) )
return;
- // do not enable native window border if JFrame should use system window decorations
- // and if not forced to use FlatLaf/JBR native window decorations
- if( !JFrame.isDefaultLookAndFeelDecorated() &&
- !UIManager.getBoolean( "TitlePane.useWindowDecorations" ) &&
- !FlatSystemProperties.getBoolean( systemPropertyKey, false ) )
- return;
-
// do not enable native window border if frame is undecorated
if( frame.isUndecorated() )
return;
@@ -119,22 +113,16 @@ public class FlatNativeWindowBorder
setHasCustomDecoration( frame, true );
// enable Swing window decoration
- frame.getRootPane().setWindowDecorationStyle( JRootPane.FRAME );
+ rootPane.setWindowDecorationStyle( JRootPane.FRAME );
} else if( window instanceof JDialog ) {
JDialog dialog = (JDialog) window;
+ JRootPane rootPane = dialog.getRootPane();
- // check whether disabled via client property
- if( !FlatClientProperties.clientPropertyBoolean( dialog.getRootPane(), FlatClientProperties.USE_WINDOW_DECORATIONS, true ) )
+ // check whether disabled via system property, client property or UI default
+ if( !useWindowDecorations( rootPane, systemPropertyKey ) )
return;
- // do not enable native window border if JDialog should use system window decorations
- // and if not forced to use FlatLaf/JBR native window decorations
- if( !JDialog.isDefaultLookAndFeelDecorated() &&
- !UIManager.getBoolean( "TitlePane.useWindowDecorations" ) &&
- !FlatSystemProperties.getBoolean( systemPropertyKey, false ) )
- return;
-
// do not enable native window border if dialog is undecorated
if( dialog.isUndecorated() )
return;
@@ -143,7 +131,7 @@ public class FlatNativeWindowBorder
setHasCustomDecoration( dialog, true );
// enable Swing window decoration
- dialog.getRootPane().setWindowDecorationStyle( JRootPane.PLAIN_DIALOG );
+ rootPane.setWindowDecorationStyle( JRootPane.PLAIN_DIALOG );
}
}
@@ -153,12 +141,15 @@ public class FlatNativeWindowBorder
return;
}
+ if( !isSupported() )
+ return;
+
// remove listener
if( data instanceof PropertyChangeListener )
rootPane.removePropertyChangeListener( "ancestor", (PropertyChangeListener) data );
- // do not uninstall when switching to another FlatLaf theme
- if( UIManager.getLookAndFeel() instanceof FlatLaf )
+ // do not uninstall when switching to another FlatLaf theme and if still enabled
+ if( UIManager.getLookAndFeel() instanceof FlatLaf && useWindowDecorations( rootPane, FlatSystemProperties.USE_WINDOW_DECORATIONS ) )
return;
// uninstall native window border
@@ -188,6 +179,20 @@ public class FlatNativeWindowBorder
}
}
+ private static boolean useWindowDecorations( JRootPane rootPane, String systemPropertyKey ) {
+ // check whether forced to enabled/disabled via system property
+ Boolean enabled = FlatSystemProperties.getBooleanStrict( systemPropertyKey, null );
+ if( enabled != null )
+ return enabled;
+
+ // check whether forced to enabled/disabled via client property
+ enabled = FlatClientProperties.clientPropertyBooleanStrict( rootPane, FlatClientProperties.USE_WINDOW_DECORATIONS, null );
+ if( enabled != null )
+ return enabled;
+
+ return UIManager.getBoolean( "TitlePane.useWindowDecorations" );
+ }
+
public static boolean hasCustomDecoration( Window window ) {
if( canUseJBRCustomDecorations )
return JBRCustomDecorations.hasCustomDecoration( window );
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 7dc26eff..a484b6cd 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
@@ -95,7 +95,7 @@ public class FlatRootPaneUI
else
installBorder();
- nativeWindowBorderData = FlatNativeWindowBorder.install( rootPane );
+ installNativeWindowBorder();
}
protected void installBorder() {
@@ -110,8 +110,7 @@ public class FlatRootPaneUI
public void uninstallUI( JComponent c ) {
super.uninstallUI( c );
- FlatNativeWindowBorder.uninstall( rootPane, nativeWindowBorderData );
-
+ uninstallNativeWindowBorder();
uninstallClientDecorations();
rootPane = null;
}
@@ -137,6 +136,34 @@ public class FlatRootPaneUI
c.putClientProperty( "jetbrains.awt.windowDarkAppearance", FlatLaf.isLafDark() );
}
+ /**
+ * @since 1.1.2
+ */
+ protected void installNativeWindowBorder() {
+ nativeWindowBorderData = FlatNativeWindowBorder.install( rootPane );
+ }
+
+ /**
+ * @since 1.1.2
+ */
+ protected void uninstallNativeWindowBorder() {
+ FlatNativeWindowBorder.uninstall( rootPane, nativeWindowBorderData );
+ nativeWindowBorderData = null;
+ }
+
+ /**
+ * @since 1.1.2
+ */
+ public static void updateNativeWindowBorder( JRootPane rootPane ) {
+ RootPaneUI rui = rootPane.getUI();
+ if( !(rui instanceof FlatRootPaneUI) )
+ return;
+
+ FlatRootPaneUI ui = (FlatRootPaneUI) rui;
+ ui.uninstallNativeWindowBorder();
+ ui.installNativeWindowBorder();
+ }
+
protected void installClientDecorations() {
boolean isNativeWindowBorderSupported = FlatNativeWindowBorder.isSupported();
@@ -218,6 +245,10 @@ public class FlatRootPaneUI
installBorder();
break;
+ case FlatClientProperties.USE_WINDOW_DECORATIONS:
+ updateNativeWindowBorder( rootPane );
+ break;
+
case FlatClientProperties.MENU_BAR_EMBEDDED:
if( titlePane != null ) {
titlePane.menuBarChanged();
diff --git a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java
index 46d826f6..0cac1876 100644
--- a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java
+++ b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java
@@ -24,7 +24,6 @@ import java.util.prefs.Preferences;
import javax.swing.*;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.StyleContext;
-import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.demo.HintManager.Hint;
import com.formdev.flatlaf.demo.extras.*;
@@ -35,7 +34,6 @@ import com.formdev.flatlaf.extras.FlatUIDefaultsInspector;
import com.formdev.flatlaf.extras.components.FlatButton;
import com.formdev.flatlaf.extras.components.FlatButton.ButtonType;
import com.formdev.flatlaf.extras.FlatSVGUtils;
-import com.formdev.flatlaf.ui.FlatNativeWindowBorder;
import com.formdev.flatlaf.ui.JBRCustomDecorations;
import net.miginfocom.layout.ConstraintParser;
import net.miginfocom.layout.LC;
@@ -144,35 +142,21 @@ class DemoFrame
private void windowDecorationsChanged() {
boolean windowDecorations = windowDecorationsCheckBoxMenuItem.isSelected();
- // change window decoration of demo main frame
- if( FlatNativeWindowBorder.isSupported() ) {
- FlatNativeWindowBorder.setHasCustomDecoration( this, windowDecorations );
- getRootPane().setWindowDecorationStyle( windowDecorations ? JRootPane.FRAME : JRootPane.NONE );
- } else {
- dispose();
- setUndecorated( windowDecorations );
- getRootPane().setWindowDecorationStyle( windowDecorations ? JRootPane.FRAME : JRootPane.NONE );
- setVisible( true );
- }
- menuBarEmbeddedCheckBoxMenuItem.setEnabled( windowDecorations );
+ // change window decoration of all frames and dialogs
+ FlatLaf.setUseNativeWindowDecorations( windowDecorations );
- // enable/disable window decoration for later created frames/dialogs
- UIManager.put( "TitlePane.useWindowDecorations", windowDecorations );
+ menuBarEmbeddedCheckBoxMenuItem.setEnabled( windowDecorations );
+ unifiedTitleBarMenuItem.setEnabled( windowDecorations );
}
private void menuBarEmbeddedChanged() {
- getRootPane().putClientProperty( FlatClientProperties.MENU_BAR_EMBEDDED,
- menuBarEmbeddedCheckBoxMenuItem.isSelected() ? null : false );
-
-// alternative method for all frames and menu bars in an application
-// UIManager.put( "TitlePane.menuBarEmbedded", menuBarEmbeddedCheckBoxMenuItem.isSelected() );
-// revalidate();
-// repaint();
+ UIManager.put( "TitlePane.menuBarEmbedded", menuBarEmbeddedCheckBoxMenuItem.isSelected() );
+ FlatLaf.revalidateAndRepaintAllFramesAndDialogs();
}
private void unifiedTitleBar() {
UIManager.put( "TitlePane.unifiedBackground", unifiedTitleBarMenuItem.isSelected() );
- repaint();
+ FlatLaf.repaintAllFramesAndDialogs();
}
private void underlineMenuSelection() {
@@ -748,8 +732,7 @@ class DemoFrame
copyMenuItem.addActionListener( new DefaultEditorKit.CopyAction() );
pasteMenuItem.addActionListener( new DefaultEditorKit.PasteAction() );
- boolean supportsWindowDecorations = UIManager.getLookAndFeel()
- .getSupportsWindowDecorations() || FlatNativeWindowBorder.isSupported();
+ boolean supportsWindowDecorations = FlatLaf.supportsNativeWindowDecorations();
// If the JetBrainsRuntime is used, it forces the use of it's own custom
// window decoration, meaning we can't use our own.
diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatNativeWindowBorderTest.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatNativeWindowBorderTest.java
index 7ece92dd..9b121bd9 100644
--- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatNativeWindowBorderTest.java
+++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatNativeWindowBorderTest.java
@@ -35,8 +35,8 @@ import com.formdev.flatlaf.FlatIntelliJLaf;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.extras.FlatInspector;
+import com.formdev.flatlaf.extras.components.FlatTriStateCheckBox;
import com.formdev.flatlaf.ui.FlatLineBorder;
-import com.formdev.flatlaf.ui.FlatNativeWindowBorder;
import com.formdev.flatlaf.util.SystemInfo;
import net.miginfocom.swing.*;
@@ -304,13 +304,11 @@ public class FlatNativeWindowBorderTest
}
private void nativeChanged() {
- FlatNativeWindowBorder.setHasCustomDecoration( window, nativeCheckBox.isSelected() );
+ FlatLaf.setUseNativeWindowDecorations( nativeCheckBox.isSelected() );
}
private void native2Changed() {
- ((RootPaneContainer)window).getRootPane().putClientProperty( FlatClientProperties.USE_WINDOW_DECORATIONS, native2CheckBox.isSelected() );
- window.dispose();
- window.setVisible( true );
+ ((RootPaneContainer)window).getRootPane().putClientProperty( FlatClientProperties.USE_WINDOW_DECORATIONS, native2CheckBox.getChecked() );
}
private void revalidateLayout() {
@@ -382,7 +380,7 @@ public class FlatNativeWindowBorderTest
undecoratedCheckBox = new JCheckBox();
fullScreenCheckBox = new JCheckBox();
nativeCheckBox = new JCheckBox();
- native2CheckBox = new JCheckBox();
+ native2CheckBox = new FlatTriStateCheckBox();
openDialogButton = new JButton();
hideWindowButton = new JButton();
reopenButton = new JButton();
@@ -446,7 +444,6 @@ public class FlatNativeWindowBorderTest
//---- native2CheckBox ----
native2CheckBox.setText("JRootPane.useWindowDecorations");
- native2CheckBox.setSelected(true);
native2CheckBox.addActionListener(e -> native2Changed());
add(native2CheckBox, "cell 0 3 3 1");
@@ -507,7 +504,7 @@ public class FlatNativeWindowBorderTest
private JCheckBox undecoratedCheckBox;
private JCheckBox fullScreenCheckBox;
private JCheckBox nativeCheckBox;
- private JCheckBox native2CheckBox;
+ private FlatTriStateCheckBox native2CheckBox;
private JButton openDialogButton;
private JButton hideWindowButton;
private JButton reopenButton;
diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatNativeWindowBorderTest.jfd b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatNativeWindowBorderTest.jfd
index 56f5faa3..deefbc5f 100644
--- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatNativeWindowBorderTest.jfd
+++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatNativeWindowBorderTest.jfd
@@ -65,10 +65,9 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3 3 1"
} )
- add( new FormComponent( "javax.swing.JCheckBox" ) {
+ add( new FormComponent( "com.formdev.flatlaf.extras.components.FlatTriStateCheckBox" ) {
name: "native2CheckBox"
"text": "JRootPane.useWindowDecorations"
- "selected": true
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "native2Changed", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3 3 1"
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 7df63667..856058ae 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
@@ -35,17 +35,9 @@ public class FlatWindowDecorationsTest
{
public static void main( String[] args ) {
SwingUtilities.invokeLater( () -> {
- // enable custom window decoration (if LaF supports it)
- JFrame.setDefaultLookAndFeelDecorated( true );
- JDialog.setDefaultLookAndFeelDecorated( true );
-
FlatTestFrame frame = FlatTestFrame.create( args, "FlatWindowDecorationsTest" );
frame.applyComponentOrientationToFrame = true;
- // WARNING: Do not this in real-world programs.
-// frame.setUndecorated( true );
-// frame.getRootPane().setWindowDecorationStyle( JRootPane.FRAME );
-
Class> cls = FlatWindowDecorationsTest.class;
List