mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-12 06:57:13 -06:00
Native window decorations: initial implementation (using JNA; will be replaced with JNI later)
This commit is contained in:
@@ -46,6 +46,7 @@ import javax.swing.ImageIcon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JRootPane;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.PopupFactory;
|
||||
import javax.swing.SwingUtilities;
|
||||
@@ -59,8 +60,8 @@ import javax.swing.plaf.UIResource;
|
||||
import javax.swing.plaf.basic.BasicLookAndFeel;
|
||||
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.JBRCustomDecorations;
|
||||
import com.formdev.flatlaf.util.GrayFilter;
|
||||
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
|
||||
import com.formdev.flatlaf.util.SystemInfo;
|
||||
@@ -146,27 +147,39 @@ public abstract class FlatLaf
|
||||
* This depends on the operating system and on the used Java runtime.
|
||||
* <p>
|
||||
* To use custom window decorations in your application, enable them with
|
||||
* following code (before creating any frames or dialogs). Then custom window
|
||||
* decorations are only enabled if this method returns {@code true}.
|
||||
* following code (before creating any frames or dialogs).
|
||||
* <pre>
|
||||
* JFrame.setDefaultLookAndFeelDecorated( true );
|
||||
* JDialog.setDefaultLookAndFeelDecorated( true );
|
||||
* </pre>
|
||||
* <p>
|
||||
* Returns {@code true} on Windows 10, {@code false} otherwise.
|
||||
* Then custom window decorations are only enabled if this method returns {@code true}.
|
||||
* In this case, when creating a {@link JFrame} or {@link JDialog}, the frame/dialog will be made
|
||||
* undecorated ({@link JFrame#setUndecorated(boolean)} / {@link JDialog#setUndecorated(boolean)}),
|
||||
* the window decoration style of the {@link JRootPane} will
|
||||
* {@link JRootPane#FRAME} / {@link JRootPane#PLAIN_DIALOG}
|
||||
* (see {@link JRootPane#setWindowDecorationStyle(int)}) and the look and feel
|
||||
* is responsible for the whole frame/dialog border (including window resizing).
|
||||
* <p>
|
||||
* Return also {@code false} if running on Windows 10 in
|
||||
* This method returns {@code true} on Windows 10 (see exception below), {@code false} otherwise.
|
||||
* <p>
|
||||
* Returns also {@code false} on Windows 10 if:
|
||||
* <ul>
|
||||
* <li>FlatLaf native window border support is available (requires {@code flatlaf-native-jna.jar})</li>
|
||||
* <li>running in
|
||||
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime 11 (or later)</a>
|
||||
* (<a href="https://github.com/JetBrains/JetBrainsRuntime">source code on github</a>)
|
||||
* and JBR supports custom window decorations. In this case, JBR custom decorations
|
||||
* are enabled if {@link JFrame#isDefaultLookAndFeelDecorated()} or
|
||||
* and JBR supports custom window decorations
|
||||
* </li>
|
||||
* </ul>
|
||||
* In this case, custom decorations are enabled by the root pane
|
||||
* if {@link JFrame#isDefaultLookAndFeelDecorated()} or
|
||||
* {@link JDialog#isDefaultLookAndFeelDecorated()} return {@code true}.
|
||||
*/
|
||||
@Override
|
||||
public boolean getSupportsWindowDecorations() {
|
||||
if( SystemInfo.isJetBrainsJVM_11_orLater &&
|
||||
SystemInfo.isWindows_10_orLater &&
|
||||
JBRCustomDecorations.isSupported() )
|
||||
if( SystemInfo.isWindows_10_orLater &&
|
||||
FlatNativeWindowBorder.isSupported() )
|
||||
return false;
|
||||
|
||||
return SystemInfo.isWindows_10_orLater;
|
||||
|
||||
@@ -71,6 +71,25 @@ public interface FlatSystemProperties
|
||||
*/
|
||||
String USE_WINDOW_DECORATIONS = "flatlaf.useWindowDecorations";
|
||||
|
||||
/**
|
||||
* Specifies whether FlatLaf native window decorations should be used
|
||||
* when creating {@code JFrame} or {@code JDialog}.
|
||||
* Requires that {@code flatlaf-native-jna.jar} is on classpath/modulepath.
|
||||
* <p>
|
||||
* Setting this to {@code true} forces using FlatLaf native window decorations
|
||||
* even if they are not enabled by the application.
|
||||
* <p>
|
||||
* Setting this to {@code false} disables using FlatLaf native window decorations.
|
||||
* <p>
|
||||
* (requires Window 10)
|
||||
* <p>
|
||||
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||
* <strong>Default</strong> none
|
||||
*
|
||||
* @since 1.1
|
||||
*/
|
||||
String USE_NATIVE_WINDOW_DECORATIONS = "flatlaf.useNativeWindowDecorations";
|
||||
|
||||
/**
|
||||
* Specifies whether JetBrains Runtime custom window decorations should be used
|
||||
* when creating {@code JFrame} or {@code JDialog}.
|
||||
@@ -81,10 +100,12 @@ public interface FlatSystemProperties
|
||||
* Setting this to {@code true} forces using JetBrains Runtime custom window decorations
|
||||
* even if they are not enabled by the application.
|
||||
* <p>
|
||||
* Setting this to {@code false} disables using JetBrains Runtime custom window decorations.
|
||||
* <p>
|
||||
* (requires Window 10)
|
||||
* <p>
|
||||
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||
* <strong>Default</strong> {@code true}
|
||||
* <strong>Default</strong> none
|
||||
*/
|
||||
String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations";
|
||||
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright 2021 FormDev Software GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Window;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JRootPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.FlatLaf;
|
||||
import com.formdev.flatlaf.FlatSystemProperties;
|
||||
import com.formdev.flatlaf.util.SystemInfo;
|
||||
|
||||
/**
|
||||
* Support for custom window decorations with native window border.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatNativeWindowBorder
|
||||
{
|
||||
// check this field before using class JBRCustomDecorations to avoid unnecessary loading of that class
|
||||
private static final boolean canUseJBRCustomDecorations
|
||||
= SystemInfo.isJetBrainsJVM_11_orLater && SystemInfo.isWindows_10_orLater;
|
||||
|
||||
private static Boolean supported;
|
||||
private static Provider nativeProvider;
|
||||
|
||||
public static boolean isSupported() {
|
||||
if( canUseJBRCustomDecorations )
|
||||
return JBRCustomDecorations.isSupported();
|
||||
|
||||
initialize();
|
||||
return supported;
|
||||
}
|
||||
|
||||
static Object install( JRootPane rootPane ) {
|
||||
if( canUseJBRCustomDecorations )
|
||||
return JBRCustomDecorations.install( rootPane );
|
||||
|
||||
if( !isSupported() )
|
||||
return null;
|
||||
|
||||
// check whether root pane already has a parent, which is the case when switching LaF
|
||||
Window window = SwingUtilities.windowForComponent( rootPane );
|
||||
if( window != null ) {
|
||||
install( window, FlatSystemProperties.USE_NATIVE_WINDOW_DECORATIONS );
|
||||
return null;
|
||||
}
|
||||
|
||||
// Install FlatLaf native window border, which must be done late,
|
||||
// when the native window is already created, because it needs access to the window.
|
||||
// "ancestor" property change event is fired from JComponent.addNotify() and removeNotify().
|
||||
PropertyChangeListener ancestorListener = e -> {
|
||||
Object newValue = e.getNewValue();
|
||||
if( newValue instanceof Window )
|
||||
install( (Window) newValue, FlatSystemProperties.USE_NATIVE_WINDOW_DECORATIONS );
|
||||
else if( newValue == null && e.getOldValue() instanceof Window )
|
||||
uninstall( (Window) e.getOldValue() );
|
||||
};
|
||||
rootPane.addPropertyChangeListener( "ancestor", ancestorListener );
|
||||
return ancestorListener;
|
||||
}
|
||||
|
||||
static void install( Window window, String systemPropertyKey ) {
|
||||
if( hasCustomDecoration( window ) )
|
||||
return;
|
||||
|
||||
// do not enable native window border if LaF provides decorations
|
||||
if( UIManager.getLookAndFeel().getSupportsWindowDecorations() )
|
||||
return;
|
||||
|
||||
if( window instanceof JFrame ) {
|
||||
JFrame frame = (JFrame) window;
|
||||
|
||||
// do not enable native window border if JFrame should use system window decorations
|
||||
// and if not forced to use TODO JBR decorations
|
||||
if( !JFrame.isDefaultLookAndFeelDecorated() &&
|
||||
!FlatSystemProperties.getBoolean( systemPropertyKey, false ))
|
||||
return;
|
||||
|
||||
// do not enable native window border if frame is undecorated
|
||||
if( frame.isUndecorated() )
|
||||
return;
|
||||
|
||||
// enable native window border for window
|
||||
setHasCustomDecoration( frame, true );
|
||||
|
||||
// enable Swing window decoration
|
||||
frame.getRootPane().setWindowDecorationStyle( JRootPane.FRAME );
|
||||
|
||||
} else if( window instanceof JDialog ) {
|
||||
JDialog dialog = (JDialog) window;
|
||||
|
||||
// do not enable native window border if JDialog should use system window decorations
|
||||
// and if not forced to use TODO JBR decorations
|
||||
if( !JDialog.isDefaultLookAndFeelDecorated() &&
|
||||
!FlatSystemProperties.getBoolean( systemPropertyKey, false ))
|
||||
return;
|
||||
|
||||
// do not enable native window border if dialog is undecorated
|
||||
if( dialog.isUndecorated() )
|
||||
return;
|
||||
|
||||
// enable native window border for window
|
||||
setHasCustomDecoration( dialog, true );
|
||||
|
||||
// enable Swing window decoration
|
||||
dialog.getRootPane().setWindowDecorationStyle( JRootPane.PLAIN_DIALOG );
|
||||
}
|
||||
}
|
||||
|
||||
static void uninstall( JRootPane rootPane, Object data ) {
|
||||
if( canUseJBRCustomDecorations ) {
|
||||
JBRCustomDecorations.uninstall( rootPane, data );
|
||||
return;
|
||||
}
|
||||
|
||||
// remove listener
|
||||
if( data instanceof PropertyChangeListener )
|
||||
rootPane.removePropertyChangeListener( "ancestor", (PropertyChangeListener) data );
|
||||
|
||||
// uninstall native window border, except when switching to another FlatLaf theme
|
||||
Window window = SwingUtilities.windowForComponent( rootPane );
|
||||
if( window != null )
|
||||
uninstall( window );
|
||||
}
|
||||
|
||||
private static void uninstall( Window window ) {
|
||||
if( !hasCustomDecoration( window ) )
|
||||
return;
|
||||
|
||||
// do not uninstall when switching to another FlatLaf theme
|
||||
if( UIManager.getLookAndFeel() instanceof FlatLaf )
|
||||
return;
|
||||
|
||||
// disable native window border for window
|
||||
setHasCustomDecoration( window, false );
|
||||
|
||||
if( window instanceof JFrame ) {
|
||||
JFrame frame = (JFrame) window;
|
||||
|
||||
// disable Swing window decoration
|
||||
frame.getRootPane().setWindowDecorationStyle( JRootPane.NONE );
|
||||
|
||||
} else if( window instanceof JDialog ) {
|
||||
JDialog dialog = (JDialog) window;
|
||||
|
||||
// disable Swing window decoration
|
||||
dialog.getRootPane().setWindowDecorationStyle( JRootPane.NONE );
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasCustomDecoration( Window window ) {
|
||||
if( canUseJBRCustomDecorations )
|
||||
return JBRCustomDecorations.hasCustomDecoration( window );
|
||||
|
||||
if( !isSupported() )
|
||||
return false;
|
||||
|
||||
return nativeProvider.hasCustomDecoration( window );
|
||||
}
|
||||
|
||||
public static void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
|
||||
if( canUseJBRCustomDecorations ) {
|
||||
JBRCustomDecorations.setHasCustomDecoration( window, hasCustomDecoration );
|
||||
return;
|
||||
}
|
||||
|
||||
if( !isSupported() )
|
||||
return;
|
||||
|
||||
nativeProvider.setHasCustomDecoration( window, hasCustomDecoration );
|
||||
}
|
||||
|
||||
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight, List<Rectangle> hitTestSpots ) {
|
||||
if( canUseJBRCustomDecorations ) {
|
||||
JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots );
|
||||
return;
|
||||
}
|
||||
|
||||
if( !isSupported() )
|
||||
return;
|
||||
|
||||
nativeProvider.setTitleBarHeight( window, titleBarHeight );
|
||||
nativeProvider.setTitleBarHitTestSpots( window, hitTestSpots );
|
||||
}
|
||||
|
||||
private static void initialize() {
|
||||
if( supported != null )
|
||||
return;
|
||||
supported = false;
|
||||
|
||||
// requires Windows 10 on x86_64
|
||||
if( !SystemInfo.isWindows_10_orLater || !SystemInfo.isX86_64 )
|
||||
return;
|
||||
|
||||
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_NATIVE_WINDOW_DECORATIONS, true ) )
|
||||
return;
|
||||
|
||||
try {
|
||||
Class<?> cls = Class.forName( "com.formdev.flatlaf.nativejna.windows.FlatWindowsNativeWindowBorder" );
|
||||
Method m = cls.getMethod( "getInstance" );
|
||||
nativeProvider = (Provider) m.invoke( null );
|
||||
|
||||
supported = (nativeProvider != null);
|
||||
} catch( Exception ex ) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
//---- interface Provider -------------------------------------------------
|
||||
|
||||
public interface Provider
|
||||
{
|
||||
boolean hasCustomDecoration( Window window );
|
||||
void setHasCustomDecoration( Window window, boolean hasCustomDecoration );
|
||||
void setTitleBarHeight( Window window, int titleBarHeight );
|
||||
void setTitleBarHitTestSpots( Window window, List<Rectangle> hitTestSpots );
|
||||
}
|
||||
}
|
||||
@@ -70,16 +70,13 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
public class FlatRootPaneUI
|
||||
extends BasicRootPaneUI
|
||||
{
|
||||
// check this field before using class JBRCustomDecorations to avoid unnecessary loading of that class
|
||||
static final boolean canUseJBRCustomDecorations
|
||||
= SystemInfo.isJetBrainsJVM_11_orLater && SystemInfo.isWindows_10_orLater;
|
||||
|
||||
protected final Color borderColor = UIManager.getColor( "TitlePane.borderColor" );
|
||||
|
||||
protected JRootPane rootPane;
|
||||
protected FlatTitlePane titlePane;
|
||||
protected FlatWindowResizer windowResizer;
|
||||
|
||||
private Object nativeWindowBorderData;
|
||||
private LayoutManager oldLayout;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
@@ -97,8 +94,7 @@ public class FlatRootPaneUI
|
||||
else
|
||||
installBorder();
|
||||
|
||||
if( canUseJBRCustomDecorations )
|
||||
JBRCustomDecorations.install( rootPane );
|
||||
nativeWindowBorderData = FlatNativeWindowBorder.install( rootPane );
|
||||
}
|
||||
|
||||
protected void installBorder() {
|
||||
@@ -113,6 +109,8 @@ public class FlatRootPaneUI
|
||||
public void uninstallUI( JComponent c ) {
|
||||
super.uninstallUI( c );
|
||||
|
||||
FlatNativeWindowBorder.uninstall( rootPane, nativeWindowBorderData );
|
||||
|
||||
uninstallClientDecorations();
|
||||
rootPane = null;
|
||||
}
|
||||
@@ -139,10 +137,10 @@ public class FlatRootPaneUI
|
||||
}
|
||||
|
||||
protected void installClientDecorations() {
|
||||
boolean isJBRSupported = canUseJBRCustomDecorations && JBRCustomDecorations.isSupported();
|
||||
boolean isNativeWindowBorderSupported = FlatNativeWindowBorder.isSupported();
|
||||
|
||||
// install border
|
||||
if( rootPane.getWindowDecorationStyle() != JRootPane.NONE && !isJBRSupported )
|
||||
if( rootPane.getWindowDecorationStyle() != JRootPane.NONE && !isNativeWindowBorderSupported )
|
||||
LookAndFeel.installBorder( rootPane, "RootPane.border" );
|
||||
else
|
||||
LookAndFeel.uninstallBorder( rootPane );
|
||||
@@ -155,7 +153,7 @@ public class FlatRootPaneUI
|
||||
rootPane.setLayout( createRootLayout() );
|
||||
|
||||
// install window resizer
|
||||
if( !isJBRSupported )
|
||||
if( !isNativeWindowBorderSupported )
|
||||
windowResizer = createWindowResizer();
|
||||
}
|
||||
|
||||
|
||||
@@ -337,7 +337,7 @@ public class FlatTitlePane
|
||||
// show/hide icon
|
||||
iconLabel.setVisible( hasIcon );
|
||||
|
||||
updateJBRHitTestSpotsAndTitleBarHeightLater();
|
||||
updateNativeTitleBarHeightAndHitTestSpotsLater();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -355,7 +355,7 @@ public class FlatTitlePane
|
||||
installWindowListeners();
|
||||
}
|
||||
|
||||
updateJBRHitTestSpotsAndTitleBarHeightLater();
|
||||
updateNativeTitleBarHeightAndHitTestSpotsLater();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -435,7 +435,7 @@ public class FlatTitlePane
|
||||
}
|
||||
|
||||
protected void menuBarLayouted() {
|
||||
updateJBRHitTestSpotsAndTitleBarHeightLater();
|
||||
updateNativeTitleBarHeightAndHitTestSpotsLater();
|
||||
}
|
||||
|
||||
/*debug
|
||||
@@ -449,8 +449,9 @@ public class FlatTitlePane
|
||||
}
|
||||
if( debugHitTestSpots != null ) {
|
||||
g.setColor( Color.blue );
|
||||
Point offset = SwingUtilities.convertPoint( this, 0, 0, window );
|
||||
for( Rectangle r : debugHitTestSpots )
|
||||
g.drawRect( r.x, r.y, r.width, r.height );
|
||||
g.drawRect( r.x - offset.x, r.y - offset.y, r.width - 1, r.height - 1 );
|
||||
}
|
||||
}
|
||||
debug*/
|
||||
@@ -503,9 +504,9 @@ debug*/
|
||||
Frame frame = (Frame) window;
|
||||
|
||||
// set maximized bounds to avoid that maximized window overlaps Windows task bar
|
||||
// (if not running in JBR and if not modified from the application)
|
||||
// (if not having native window border and if not modified from the application)
|
||||
Rectangle oldMaximizedBounds = frame.getMaximizedBounds();
|
||||
if( !hasJBRCustomDecoration() &&
|
||||
if( !hasNativeCustomDecoration() &&
|
||||
(oldMaximizedBounds == null ||
|
||||
Objects.equals( oldMaximizedBounds, rootPane.getClientProperty( "_flatlaf.maximizedBounds" ) )) )
|
||||
{
|
||||
@@ -601,46 +602,51 @@ debug*/
|
||||
window.dispatchEvent( new WindowEvent( window, WindowEvent.WINDOW_CLOSING ) );
|
||||
}
|
||||
|
||||
protected boolean hasJBRCustomDecoration() {
|
||||
return FlatRootPaneUI.canUseJBRCustomDecorations &&
|
||||
window != null &&
|
||||
JBRCustomDecorations.hasCustomDecoration( window );
|
||||
private boolean hasJBRCustomDecoration() {
|
||||
return window != null && JBRCustomDecorations.hasCustomDecoration( window );
|
||||
}
|
||||
|
||||
protected void updateJBRHitTestSpotsAndTitleBarHeightLater() {
|
||||
/**
|
||||
* Returns whether windows uses native window border and has custom decorations enabled.
|
||||
*/
|
||||
protected boolean hasNativeCustomDecoration() {
|
||||
return window != null && FlatNativeWindowBorder.hasCustomDecoration( window );
|
||||
}
|
||||
|
||||
protected void updateNativeTitleBarHeightAndHitTestSpotsLater() {
|
||||
EventQueue.invokeLater( () -> {
|
||||
updateJBRHitTestSpotsAndTitleBarHeight();
|
||||
updateNativeTitleBarHeightAndHitTestSpots();
|
||||
} );
|
||||
}
|
||||
|
||||
protected void updateJBRHitTestSpotsAndTitleBarHeight() {
|
||||
protected void updateNativeTitleBarHeightAndHitTestSpots() {
|
||||
if( !isDisplayable() )
|
||||
return;
|
||||
|
||||
if( !hasJBRCustomDecoration() )
|
||||
if( !hasNativeCustomDecoration() )
|
||||
return;
|
||||
|
||||
List<Rectangle> hitTestSpots = new ArrayList<>();
|
||||
if( iconLabel.isVisible() )
|
||||
addJBRHitTestSpot( iconLabel, false, hitTestSpots );
|
||||
addJBRHitTestSpot( buttonPanel, false, hitTestSpots );
|
||||
addJBRHitTestSpot( menuBarPlaceholder, true, hitTestSpots );
|
||||
|
||||
int titleBarHeight = getHeight();
|
||||
// slightly reduce height so that component receives mouseExit events
|
||||
if( titleBarHeight > 0 )
|
||||
titleBarHeight--;
|
||||
|
||||
JBRCustomDecorations.setHitTestSpotsAndTitleBarHeight( window, hitTestSpots, titleBarHeight );
|
||||
List<Rectangle> hitTestSpots = new ArrayList<>();
|
||||
if( iconLabel.isVisible() )
|
||||
addNativeHitTestSpot( iconLabel, false, hitTestSpots );
|
||||
addNativeHitTestSpot( buttonPanel, false, hitTestSpots );
|
||||
addNativeHitTestSpot( menuBarPlaceholder, true, hitTestSpots );
|
||||
|
||||
FlatNativeWindowBorder.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots );
|
||||
|
||||
/*debug
|
||||
debugHitTestSpots = hitTestSpots;
|
||||
debugTitleBarHeight = titleBarHeight;
|
||||
debugHitTestSpots = hitTestSpots;
|
||||
repaint();
|
||||
debug*/
|
||||
}
|
||||
|
||||
protected void addJBRHitTestSpot( JComponent c, boolean subtractMenuBarMargins, List<Rectangle> hitTestSpots ) {
|
||||
protected void addNativeHitTestSpot( JComponent c, boolean subtractMenuBarMargins, List<Rectangle> hitTestSpots ) {
|
||||
Dimension size = c.getSize();
|
||||
if( size.width <= 0 || size.height <= 0 )
|
||||
return;
|
||||
@@ -655,8 +661,8 @@ debug*/
|
||||
}
|
||||
|
||||
/*debug
|
||||
private List<Rectangle> debugHitTestSpots;
|
||||
private int debugTitleBarHeight;
|
||||
private List<Rectangle> debugHitTestSpots;
|
||||
debug*/
|
||||
|
||||
//---- class TitlePaneBorder ----------------------------------------------
|
||||
@@ -730,7 +736,7 @@ debug*/
|
||||
break;
|
||||
|
||||
case "componentOrientation":
|
||||
updateJBRHitTestSpotsAndTitleBarHeightLater();
|
||||
updateNativeTitleBarHeightAndHitTestSpotsLater();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -740,7 +746,7 @@ debug*/
|
||||
@Override
|
||||
public void windowActivated( WindowEvent e ) {
|
||||
activeChanged( true );
|
||||
updateJBRHitTestSpotsAndTitleBarHeight();
|
||||
updateNativeTitleBarHeightAndHitTestSpots();
|
||||
|
||||
if( hasJBRCustomDecoration() )
|
||||
JBRWindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
|
||||
@@ -751,7 +757,7 @@ debug*/
|
||||
@Override
|
||||
public void windowDeactivated( WindowEvent e ) {
|
||||
activeChanged( false );
|
||||
updateJBRHitTestSpotsAndTitleBarHeight();
|
||||
updateNativeTitleBarHeightAndHitTestSpots();
|
||||
|
||||
if( hasJBRCustomDecoration() )
|
||||
JBRWindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
|
||||
@@ -762,7 +768,7 @@ debug*/
|
||||
@Override
|
||||
public void windowStateChanged( WindowEvent e ) {
|
||||
frameStateChanged();
|
||||
updateJBRHitTestSpotsAndTitleBarHeight();
|
||||
updateNativeTitleBarHeightAndHitTestSpots();
|
||||
}
|
||||
|
||||
//---- interface MouseListener ----
|
||||
@@ -775,7 +781,7 @@ debug*/
|
||||
if( e.getSource() == iconLabel ) {
|
||||
// double-click on icon closes window
|
||||
close();
|
||||
} else if( !hasJBRCustomDecoration() &&
|
||||
} else if( !hasNativeCustomDecoration() &&
|
||||
window instanceof Frame &&
|
||||
((Frame)window).isResizable() )
|
||||
{
|
||||
@@ -808,8 +814,8 @@ debug*/
|
||||
if( window == null )
|
||||
return; // should newer occur
|
||||
|
||||
if( hasJBRCustomDecoration() )
|
||||
return; // do nothing if running in JBR
|
||||
if( hasNativeCustomDecoration() )
|
||||
return; // do nothing if having native window border
|
||||
|
||||
// restore window if it is maximized
|
||||
if( window instanceof Frame ) {
|
||||
@@ -852,7 +858,7 @@ debug*/
|
||||
|
||||
@Override
|
||||
public void componentResized( ComponentEvent e ) {
|
||||
updateJBRHitTestSpotsAndTitleBarHeightLater();
|
||||
updateNativeTitleBarHeightAndHitTestSpotsLater();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -29,11 +29,10 @@ import java.awt.event.HierarchyEvent;
|
||||
import java.awt.event.HierarchyListener;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JRootPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
@@ -55,26 +54,29 @@ import com.formdev.flatlaf.util.SystemInfo;
|
||||
*/
|
||||
public class JBRCustomDecorations
|
||||
{
|
||||
private static boolean initialized;
|
||||
private static Boolean supported;
|
||||
private static Method Window_hasCustomDecoration;
|
||||
private static Method Window_setHasCustomDecoration;
|
||||
private static Method WWindowPeer_setCustomDecorationHitTestSpots;
|
||||
private static Method WWindowPeer_setCustomDecorationTitleBarHeight;
|
||||
private static Method WWindowPeer_setCustomDecorationHitTestSpots;
|
||||
private static Method AWTAccessor_getComponentAccessor;
|
||||
private static Method AWTAccessor_ComponentAccessor_getPeer;
|
||||
|
||||
public static boolean isSupported() {
|
||||
initialize();
|
||||
return Window_setHasCustomDecoration != null;
|
||||
return supported;
|
||||
}
|
||||
|
||||
static void install( JRootPane rootPane ) {
|
||||
static Object install( JRootPane rootPane ) {
|
||||
if( !isSupported() )
|
||||
return;
|
||||
return null;
|
||||
|
||||
// check whether root pane already has a parent, which is the case when switching LaF
|
||||
if( rootPane.getParent() != null )
|
||||
return;
|
||||
Window window = SwingUtilities.windowForComponent( rootPane );
|
||||
if( window != null ) {
|
||||
FlatNativeWindowBorder.install( window, FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS );
|
||||
return null;
|
||||
}
|
||||
|
||||
// Use hierarchy listener to wait until the root pane is added to a window.
|
||||
// Enabling JBR decorations must be done very early, probably before
|
||||
@@ -88,8 +90,9 @@ public class JBRCustomDecorations
|
||||
|
||||
Container parent = e.getChangedParent();
|
||||
if( parent instanceof Window )
|
||||
install( (Window) parent );
|
||||
FlatNativeWindowBorder.install( (Window) parent, FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS );
|
||||
|
||||
// remove listener since it is actually not possible to uninstall JBR decorations
|
||||
// use invokeLater to remove listener to avoid that listener
|
||||
// is removed while listener queue is processed
|
||||
EventQueue.invokeLater( () -> {
|
||||
@@ -98,54 +101,20 @@ public class JBRCustomDecorations
|
||||
}
|
||||
};
|
||||
rootPane.addHierarchyListener( addListener );
|
||||
return addListener;
|
||||
}
|
||||
|
||||
static void install( Window window ) {
|
||||
if( !isSupported() )
|
||||
return;
|
||||
static void uninstall( JRootPane rootPane, Object data ) {
|
||||
// remove listener (if not yet done)
|
||||
if( data instanceof HierarchyListener )
|
||||
rootPane.removeHierarchyListener( (HierarchyListener) data );
|
||||
|
||||
// do not enable JBR decorations if LaF provides decorations
|
||||
if( UIManager.getLookAndFeel().getSupportsWindowDecorations() )
|
||||
return;
|
||||
|
||||
if( window instanceof JFrame ) {
|
||||
JFrame frame = (JFrame) window;
|
||||
|
||||
// do not enable JBR decorations if JFrame should use system window decorations
|
||||
// and if not forced to use JBR decorations
|
||||
if( !JFrame.isDefaultLookAndFeelDecorated() &&
|
||||
!FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, false ))
|
||||
return;
|
||||
|
||||
// do not enable JBR decorations if frame is undecorated
|
||||
if( frame.isUndecorated() )
|
||||
return;
|
||||
|
||||
// enable JBR custom window decoration for window
|
||||
setHasCustomDecoration( frame );
|
||||
|
||||
// enable Swing window decoration
|
||||
frame.getRootPane().setWindowDecorationStyle( JRootPane.FRAME );
|
||||
|
||||
} else if( window instanceof JDialog ) {
|
||||
JDialog dialog = (JDialog) window;
|
||||
|
||||
// do not enable JBR decorations if JDialog should use system window decorations
|
||||
// and if not forced to use JBR decorations
|
||||
if( !JDialog.isDefaultLookAndFeelDecorated() &&
|
||||
!FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, false ))
|
||||
return;
|
||||
|
||||
// do not enable JBR decorations if dialog is undecorated
|
||||
if( dialog.isUndecorated() )
|
||||
return;
|
||||
|
||||
// enable JBR custom window decoration for window
|
||||
setHasCustomDecoration( dialog );
|
||||
|
||||
// enable Swing window decoration
|
||||
dialog.getRootPane().setWindowDecorationStyle( JRootPane.PLAIN_DIALOG );
|
||||
}
|
||||
// since it is actually not possible to uninstall JBR decorations,
|
||||
// simply reduce titleBarHeight so that it is still possible to resize window
|
||||
// and remove hitTestSpots
|
||||
Window window = SwingUtilities.windowForComponent( rootPane );
|
||||
if( window != null )
|
||||
setHasCustomDecoration( window, false );
|
||||
}
|
||||
|
||||
static boolean hasCustomDecoration( Window window ) {
|
||||
@@ -160,35 +129,38 @@ public class JBRCustomDecorations
|
||||
}
|
||||
}
|
||||
|
||||
static void setHasCustomDecoration( Window window ) {
|
||||
static void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
|
||||
if( !isSupported() )
|
||||
return;
|
||||
|
||||
try {
|
||||
Window_setHasCustomDecoration.invoke( window );
|
||||
if( hasCustomDecoration )
|
||||
Window_setHasCustomDecoration.invoke( window );
|
||||
else
|
||||
setTitleBarHeightAndHitTestSpots( window, 4, Collections.emptyList() );
|
||||
} catch( Exception ex ) {
|
||||
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
static void setHitTestSpotsAndTitleBarHeight( Window window, List<Rectangle> hitTestSpots, int titleBarHeight ) {
|
||||
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight, List<Rectangle> hitTestSpots ) {
|
||||
if( !isSupported() )
|
||||
return;
|
||||
|
||||
try {
|
||||
Object compAccessor = AWTAccessor_getComponentAccessor.invoke( null );
|
||||
Object peer = AWTAccessor_ComponentAccessor_getPeer.invoke( compAccessor, window );
|
||||
WWindowPeer_setCustomDecorationHitTestSpots.invoke( peer, hitTestSpots );
|
||||
WWindowPeer_setCustomDecorationTitleBarHeight.invoke( peer, titleBarHeight );
|
||||
WWindowPeer_setCustomDecorationHitTestSpots.invoke( peer, hitTestSpots );
|
||||
} catch( Exception ex ) {
|
||||
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
private static void initialize() {
|
||||
if( initialized )
|
||||
if( supported != null )
|
||||
return;
|
||||
initialized = true;
|
||||
supported = false;
|
||||
|
||||
// requires JetBrains Runtime 11 and Windows 10
|
||||
if( !SystemInfo.isJetBrainsJVM_11_orLater || !SystemInfo.isWindows_10_orLater )
|
||||
@@ -204,15 +176,17 @@ public class JBRCustomDecorations
|
||||
AWTAccessor_ComponentAccessor_getPeer = compAccessorClass.getDeclaredMethod( "getPeer", Component.class );
|
||||
|
||||
Class<?> peerClass = Class.forName( "sun.awt.windows.WWindowPeer" );
|
||||
WWindowPeer_setCustomDecorationHitTestSpots = peerClass.getDeclaredMethod( "setCustomDecorationHitTestSpots", List.class );
|
||||
WWindowPeer_setCustomDecorationTitleBarHeight = peerClass.getDeclaredMethod( "setCustomDecorationTitleBarHeight", int.class );
|
||||
WWindowPeer_setCustomDecorationHitTestSpots.setAccessible( true );
|
||||
WWindowPeer_setCustomDecorationHitTestSpots = peerClass.getDeclaredMethod( "setCustomDecorationHitTestSpots", List.class );
|
||||
WWindowPeer_setCustomDecorationTitleBarHeight.setAccessible( true );
|
||||
WWindowPeer_setCustomDecorationHitTestSpots.setAccessible( true );
|
||||
|
||||
Window_hasCustomDecoration = Window.class.getDeclaredMethod( "hasCustomDecoration" );
|
||||
Window_setHasCustomDecoration = Window.class.getDeclaredMethod( "setHasCustomDecoration" );
|
||||
Window_hasCustomDecoration.setAccessible( true );
|
||||
Window_setHasCustomDecoration.setAccessible( true );
|
||||
|
||||
supported = true;
|
||||
} catch( Exception ex ) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@@ -38,6 +38,9 @@ public class SystemInfo
|
||||
public static final boolean isMacOS_10_14_Mojave_orLater;
|
||||
public static final boolean isMacOS_10_15_Catalina_orLater;
|
||||
|
||||
// OS architecture
|
||||
public static final boolean isX86_64;
|
||||
|
||||
// Java versions
|
||||
public static final long javaVersion;
|
||||
public static final boolean isJava_9_orLater;
|
||||
@@ -65,6 +68,10 @@ public class SystemInfo
|
||||
isMacOS_10_14_Mojave_orLater = (isMacOS && osVersion >= toVersion( 10, 14, 0, 0 ));
|
||||
isMacOS_10_15_Catalina_orLater = (isMacOS && osVersion >= toVersion( 10, 15, 0, 0 ));
|
||||
|
||||
// OS architecture
|
||||
String osArch = System.getProperty( "os.arch" );
|
||||
isX86_64 = osArch.equals( "amd64" ) || osArch.equals( "x86_64" );
|
||||
|
||||
// Java versions
|
||||
javaVersion = scanVersion( System.getProperty( "java.version" ) );
|
||||
isJava_9_orLater = (javaVersion >= toVersion( 9, 0, 0, 0 ));
|
||||
|
||||
Reference in New Issue
Block a user