running in
* JetBrains Runtime 11 (or later)
* (source code on github)
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 45a24b2d..3e2fbed3 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatSystemProperties.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatSystemProperties.java
@@ -74,14 +74,13 @@ public interface FlatSystemProperties
/**
* Specifies whether FlatLaf native window decorations should be used
* when creating {@code JFrame} or {@code JDialog}.
- * Requires that {@code flatlaf-natives-jna.jar} is on classpath/modulepath.
*
* Setting this to {@code true} forces using FlatLaf native window decorations
* even if they are not enabled by the application.
*
* Setting this to {@code false} disables using FlatLaf native window decorations.
*
- * (requires Window 10)
+ * (requires Window 10 64bit)
*
* Allowed Values {@code false} and {@code true}
* Default none
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 63e62ee1..ff895609 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
@@ -20,7 +20,6 @@ import java.awt.Color;
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;
@@ -228,9 +227,12 @@ public class FlatNativeWindowBorder
return;
try {
+/*
Class> cls = Class.forName( "com.formdev.flatlaf.natives.jna.windows.FlatWindowsNativeWindowBorder" );
Method m = cls.getMethod( "getInstance" );
nativeProvider = (Provider) m.invoke( null );
+*/
+ nativeProvider = FlatWindowsNativeWindowBorder.getInstance();
supported = (nativeProvider != null);
} catch( Exception ex ) {
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatWindowsNativeWindowBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatWindowsNativeWindowBorder.java
new file mode 100644
index 00000000..4adc6109
--- /dev/null
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatWindowsNativeWindowBorder.java
@@ -0,0 +1,380 @@
+/*
+ * 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.Color;
+import java.awt.Dialog;
+import java.awt.EventQueue;
+import java.awt.Frame;
+import java.awt.GraphicsConfiguration;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Window;
+import java.awt.geom.AffineTransform;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.Timer;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+import com.formdev.flatlaf.FlatLaf;
+import com.formdev.flatlaf.util.NativeLibrary;
+import com.formdev.flatlaf.util.SystemInfo;
+
+//
+// Interesting resources:
+// https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
+// https://docs.microsoft.com/en-us/windows/win32/dwm/customframe
+// https://github.com/JetBrains/JetBrainsRuntime/blob/master/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp
+// https://github.com/JetBrains/JetBrainsRuntime/commit/d2820524a1aa211b1c49b30f659b9b4d07a6f96e
+// https://github.com/JetBrains/JetBrainsRuntime/pull/18
+// https://medium.com/swlh/customizing-the-title-bar-of-an-application-window-50a4ac3ed27e
+// https://github.com/kalbetredev/CustomDecoratedJFrame
+// https://github.com/Guerra24/NanoUI-win32
+// https://github.com/oberth/custom-chrome
+// https://github.com/rossy/borderless-window
+//
+
+/**
+ * Native window border support for Windows 10 when using custom decorations.
+ *
+ * If the application wants to use custom decorations, the Windows 10 title bar is hidden
+ * (including minimize, maximize and close buttons), but not the resize borders (including drop shadow).
+ * Windows 10 window snapping functionality will remain unaffected:
+ * https://support.microsoft.com/en-us/windows/snap-your-windows-885a9b1e-a983-a3b1-16cd-c531795e6241
+ *
+ * @author Karl Tauber
+ */
+class FlatWindowsNativeWindowBorder
+ implements FlatNativeWindowBorder.Provider
+{
+ private final Map windowsMap = Collections.synchronizedMap( new IdentityHashMap<>() );
+ private final EventListenerList listenerList = new EventListenerList();
+ private Timer fireStateChangedTimer;
+
+ private boolean colorizationUpToDate;
+ private boolean colorizationColorAffectsBorders;
+ private Color colorizationColor;
+ private int colorizationColorBalance;
+
+ private static NativeLibrary nativeLibrary;
+ private static FlatWindowsNativeWindowBorder instance;
+
+ static FlatNativeWindowBorder.Provider getInstance() {
+ // requires Windows 10 on x86_64
+ if( !SystemInfo.isWindows_10_orLater || !SystemInfo.isX86_64 )
+ return null;
+
+ // load native library
+ if( nativeLibrary == null ) {
+ if( !SystemInfo.isJava_9_orLater ) {
+ // In Java 8, load jawt.dll (part of JRE) explicitly because it
+ // is not found when running application with /bin/java.exe.
+ // When using /jre/bin/java.exe, it is found.
+ // jawt.dll is located in /jre/bin/.
+ // Java 9 and later does not have this problem.
+ try {
+ System.loadLibrary( "jawt" );
+ } catch( Exception ex ) {
+ Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
+ }
+ }
+
+ nativeLibrary = new NativeLibrary(
+ "com/formdev/flatlaf/natives/flatlaf-windows-x86_64",
+ FlatWindowsNativeWindowBorder.class.getClassLoader(), true );
+ }
+
+ // check whether native library was successfully loaded
+ if( !nativeLibrary.isLoaded() )
+ return null;
+
+ // create new instance
+ if( instance == null )
+ instance = new FlatWindowsNativeWindowBorder();
+ return instance;
+ }
+
+ private FlatWindowsNativeWindowBorder() {
+ }
+
+ @Override
+ public boolean hasCustomDecoration( Window window ) {
+ return windowsMap.containsKey( window );
+ }
+
+ /**
+ * Tell the window whether the application wants use custom decorations.
+ * If {@code true}, the Windows 10 title bar is hidden (including minimize,
+ * maximize and close buttons), but not the resize borders (including drop shadow).
+ */
+ @Override
+ public void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
+ if( hasCustomDecoration )
+ install( window );
+ else
+ uninstall( window );
+ }
+
+ private void install( Window window ) {
+ // requires Windows 10 on x86_64
+ if( !SystemInfo.isWindows_10_orLater || !SystemInfo.isX86_64 )
+ return;
+
+ // only JFrame and JDialog are supported
+ if( !(window instanceof JFrame) && !(window instanceof JDialog) )
+ return;
+
+ // not supported if frame/dialog is undecorated
+ if( (window instanceof Frame && ((Frame)window).isUndecorated()) ||
+ (window instanceof Dialog && ((Dialog)window).isUndecorated()) )
+ return;
+
+ // check whether already installed
+ if( windowsMap.containsKey( window ) )
+ return;
+
+ // install
+ WndProc wndProc = new WndProc( window );
+ if( wndProc.hwnd == 0 )
+ return;
+
+ windowsMap.put( window, wndProc );
+ }
+
+ private void uninstall( Window window ) {
+ WndProc wndProc = windowsMap.remove( window );
+ if( wndProc != null )
+ wndProc.uninstall();
+ }
+
+ @Override
+ public void setTitleBarHeight( Window window, int titleBarHeight ) {
+ WndProc wndProc = windowsMap.get( window );
+ if( wndProc == null )
+ return;
+
+ wndProc.titleBarHeight = titleBarHeight;
+ }
+
+ @Override
+ public void setTitleBarHitTestSpots( Window window, List hitTestSpots ) {
+ WndProc wndProc = windowsMap.get( window );
+ if( wndProc == null )
+ return;
+
+ wndProc.hitTestSpots = hitTestSpots.toArray( new Rectangle[hitTestSpots.size()] );
+ }
+
+ @Override
+ public void setTitleBarAppIconBounds( Window window, Rectangle appIconBounds ) {
+ WndProc wndProc = windowsMap.get( window );
+ if( wndProc == null )
+ return;
+
+ wndProc.appIconBounds = (appIconBounds != null) ? new Rectangle( appIconBounds ) : null;
+ }
+
+ @Override
+ public boolean isColorizationColorAffectsBorders() {
+ updateColorization();
+ return colorizationColorAffectsBorders;
+ }
+
+ @Override
+ public Color getColorizationColor() {
+ updateColorization();
+ return colorizationColor;
+ }
+
+ @Override
+ public int getColorizationColorBalance() {
+ updateColorization();
+ return colorizationColorBalance;
+ }
+
+ private void updateColorization() {
+ if( colorizationUpToDate )
+ return;
+ colorizationUpToDate = true;
+
+ String subKey = "SOFTWARE\\Microsoft\\Windows\\DWM";
+
+ int value = registryGetIntValue( subKey, "ColorPrevalence", -1 );
+ colorizationColorAffectsBorders = (value > 0);
+
+ value = registryGetIntValue( subKey, "ColorizationColor", -1 );
+ colorizationColor = (value != -1) ? new Color( value ) : null;
+
+ colorizationColorBalance = registryGetIntValue( subKey, "ColorizationColorBalance", -1 );
+ }
+
+ private native static int registryGetIntValue( String key, String valueName, int defaultValue );
+
+ @Override
+ public void addChangeListener( ChangeListener l ) {
+ listenerList.add( ChangeListener.class, l );
+ }
+
+ @Override
+ public void removeChangeListener( ChangeListener l ) {
+ listenerList.remove( ChangeListener.class, l );
+ }
+
+ private void fireStateChanged() {
+ Object[] listeners = listenerList.getListenerList();
+ if( listeners.length == 0 )
+ return;
+
+ ChangeEvent e = new ChangeEvent( this );
+ for( int i = 0; i < listeners.length; i += 2 ) {
+ if( listeners[i] == ChangeListener.class )
+ ((ChangeListener)listeners[i+1]).stateChanged( e );
+ }
+ }
+
+ /**
+ * Because there may be sent many WM_DWMCOLORIZATIONCOLORCHANGED messages,
+ * slightly delay event firing and fire it only once (on the AWT thread).
+ */
+ void fireStateChangedLaterOnce() {
+ EventQueue.invokeLater( () -> {
+ if( fireStateChangedTimer != null ) {
+ fireStateChangedTimer.restart();
+ return;
+ }
+
+ fireStateChangedTimer = new Timer( 300, e -> {
+ fireStateChangedTimer = null;
+ colorizationUpToDate = false;
+
+ fireStateChanged();
+ } );
+ fireStateChangedTimer.setRepeats( false );
+ fireStateChangedTimer.start();
+ } );
+ }
+
+ //---- class WndProc ------------------------------------------------------
+
+ private class WndProc
+ {
+ // WM_NCHITTEST mouse position codes
+ private static final int
+ HTCLIENT = 1,
+ HTCAPTION = 2,
+ HTSYSMENU = 3,
+ HTTOP = 12;
+
+ private Window window;
+ private final long hwnd;
+
+ private int titleBarHeight;
+ private Rectangle[] hitTestSpots;
+ private Rectangle appIconBounds;
+
+ WndProc( Window window ) {
+ this.window = window;
+
+ hwnd = installImpl( window );
+ }
+
+ void uninstall() {
+ uninstallImpl( hwnd );
+
+ // cleanup
+ window = null;
+ }
+
+ private native long installImpl( Window window );
+ private native void uninstallImpl( long hwnd );
+
+ // invoked from native code
+ private int onNcHitTest( int x, int y, boolean isOnResizeBorder ) {
+ // scale-down mouse x/y
+ Point pt = scaleDown( x, y );
+ int sx = pt.x;
+ int sy = pt.y;
+
+ // return HTSYSMENU if mouse is over application icon
+ // - left-click on HTSYSMENU area shows system menu
+ // - double-left-click sends WM_CLOSE
+ if( appIconBounds != null && appIconBounds.contains( sx, sy ) )
+ return HTSYSMENU;
+
+ boolean isOnTitleBar = (sy < titleBarHeight);
+
+ if( isOnTitleBar ) {
+ // use a second reference to the array to avoid that it can be changed
+ // in another thread while processing the array
+ Rectangle[] hitTestSpots2 = hitTestSpots;
+ for( Rectangle spot : hitTestSpots2 ) {
+ if( spot.contains( sx, sy ) )
+ return HTCLIENT;
+ }
+ return isOnResizeBorder ? HTTOP : HTCAPTION;
+ }
+
+ return isOnResizeBorder ? HTTOP : HTCLIENT;
+ }
+
+ /**
+ * Scales down in the same way as AWT.
+ * See AwtWin32GraphicsDevice::ScaleDownX() and ::ScaleDownY()
+ */
+ private Point scaleDown( int x, int y ) {
+ GraphicsConfiguration gc = window.getGraphicsConfiguration();
+ if( gc == null )
+ return new Point( x, y );
+
+ AffineTransform t = gc.getDefaultTransform();
+ return new Point( clipRound( x / t.getScaleX() ), clipRound( y / t.getScaleY() ) );
+ }
+
+ /**
+ * Rounds in the same way as AWT.
+ * See AwtWin32GraphicsDevice::ClipRound()
+ */
+ private int clipRound( double value ) {
+ value -= 0.5;
+ if( value < Integer.MIN_VALUE )
+ return Integer.MIN_VALUE;
+ if( value > Integer.MAX_VALUE )
+ return Integer.MAX_VALUE;
+ return (int) Math.ceil( value );
+ }
+
+ // invoked from native code
+ private boolean isFullscreen() {
+ GraphicsConfiguration gc = window.getGraphicsConfiguration();
+ if( gc == null )
+ return false;
+ return gc.getDevice().getFullScreenWindow() == window;
+ }
+
+ // invoked from native code
+ private void fireStateChangedLaterOnce() {
+ FlatWindowsNativeWindowBorder.this.fireStateChangedLaterOnce();
+ }
+ }
+}
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/NativeLibrary.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/NativeLibrary.java
new file mode 100644
index 00000000..ee826841
--- /dev/null
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/NativeLibrary.java
@@ -0,0 +1,127 @@
+/*
+ * 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.util;
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import com.formdev.flatlaf.FlatLaf;
+
+/**
+ * Helper class to load native library (.dll, .so or .dylib) stored in Jar.
+ *
+ * Copies native library to users temporary folder before loading it.
+ *
+ * @author Karl Tauber
+ */
+public class NativeLibrary
+{
+ private final boolean loaded;
+
+ /**
+ * Load native library from given classloader.
+ *
+ * @param libraryName resource name of the native library (without "lib" prefix and without extension)
+ * @param classLoader the classloader used to locate the library
+ * @param supported whether the native library is supported on the current platform
+ */
+ public NativeLibrary( String libraryName, ClassLoader classLoader, boolean supported ) {
+ this.loaded = supported
+ ? loadLibraryFromJar( libraryName, classLoader )
+ : false;
+ }
+
+ /**
+ * Returns whether the native library is loaded.
+ *
+ * Returns {@code false} if not supported on current platform as specified in constructor
+ * or if loading failed.
+ */
+ public boolean isLoaded() {
+ return loaded;
+ }
+
+ private static boolean loadLibraryFromJar( String libraryName, ClassLoader classLoader ) {
+ // add prefix and suffix to library name
+ libraryName = decorateLibraryName( libraryName );
+
+ // find library
+ URL libraryUrl = classLoader.getResource( libraryName );
+ if( libraryUrl == null ) {
+ log( "Library '" + libraryName + "' not found", null );
+ return false;
+ }
+
+ try {
+ // for development environment
+ if( "file".equals( libraryUrl.getProtocol() ) ) {
+ File libraryFile = new File( libraryUrl.getPath() );
+ if( libraryFile.isFile() ) {
+ // load library without copying
+ System.load( libraryFile.getCanonicalPath() );
+ return true;
+ }
+ }
+
+ // create temporary file
+ Path tempPath = Files.createTempFile( "jni", basename( libraryName ) );
+ File tempFile = tempPath.toFile();
+
+ //TODO this does not work on Windows
+ tempFile.deleteOnExit();
+
+ // copy library to temporary file
+ try( InputStream in = libraryUrl.openStream() ) {
+ Files.copy( in, tempPath, StandardCopyOption.REPLACE_EXISTING );
+ }
+
+ // load library
+ System.load( tempFile.getCanonicalPath() );
+
+ return true;
+ } catch( Throwable ex ) {
+ log( null, ex );
+ return false;
+ }
+ }
+
+ private static String decorateLibraryName( String libraryName ) {
+ if( SystemInfo.isWindows )
+ return libraryName.concat( ".dll" );
+
+ String suffix = SystemInfo.isMacOS ? ".dylib" : ".so";
+
+ int sep = libraryName.lastIndexOf( '/' );
+ return (sep >= 0)
+ ? libraryName.substring( 0, sep + 1 ) + "lib" + libraryName.substring( sep + 1 ) + suffix
+ : "lib" + libraryName + suffix;
+ }
+
+ private static String basename( String libName ) {
+ int sep = libName.lastIndexOf( '/' );
+ return (sep >= 0) ? libName.substring( sep + 1 ) : libName;
+ }
+
+ private static void log( String msg, Throwable thrown ) {
+ Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, msg, thrown );
+ }
+}
diff --git a/flatlaf-demo/build.gradle.kts b/flatlaf-demo/build.gradle.kts
index 2d3d4f2f..fd7b511f 100644
--- a/flatlaf-demo/build.gradle.kts
+++ b/flatlaf-demo/build.gradle.kts
@@ -27,19 +27,19 @@ repositories {
dependencies {
implementation( project( ":flatlaf-core" ) )
- implementation( project( ":flatlaf-natives-jna" ) )
implementation( project( ":flatlaf-extras" ) )
implementation( project( ":flatlaf-intellij-themes" ) )
implementation( "com.miglayout:miglayout-swing:5.3-SNAPSHOT" )
implementation( "com.jgoodies:jgoodies-forms:1.9.0" )
+// implementation( project( ":flatlaf-natives-jna" ) )
}
tasks {
jar {
dependsOn( ":flatlaf-core:jar" )
- dependsOn( ":flatlaf-natives-jna:jar" )
dependsOn( ":flatlaf-extras:jar" )
dependsOn( ":flatlaf-intellij-themes:jar" )
+// dependsOn( ":flatlaf-natives-jna:jar" )
manifest {
attributes( "Main-Class" to "com.formdev.flatlaf.demo.FlatLafDemo" )
diff --git a/flatlaf-natives/flatlaf-natives-jna/src/main/java/com/formdev/flatlaf/natives/jna/windows/FlatWindowsNativeWindowBorder.java b/flatlaf-natives/flatlaf-natives-jna/src/main/java/com/formdev/flatlaf/natives/jna/windows/FlatWindowsNativeWindowBorder.java
index 07d09bff..b4d661a0 100644
--- a/flatlaf-natives/flatlaf-natives-jna/src/main/java/com/formdev/flatlaf/natives/jna/windows/FlatWindowsNativeWindowBorder.java
+++ b/flatlaf-natives/flatlaf-natives-jna/src/main/java/com/formdev/flatlaf/natives/jna/windows/FlatWindowsNativeWindowBorder.java
@@ -207,20 +207,20 @@ public class FlatWindowsNativeWindowBorder
String subKey = "SOFTWARE\\Microsoft\\Windows\\DWM";
- int value = RegGetDword( HKEY_CURRENT_USER, subKey, "ColorPrevalence" );
+ int value = registryGetIntValue( subKey, "ColorPrevalence", -1 );
colorizationColorAffectsBorders = (value > 0);
- value = RegGetDword( HKEY_CURRENT_USER, subKey, "ColorizationColor" );
+ value = registryGetIntValue( subKey, "ColorizationColor", -1 );
colorizationColor = (value != -1) ? new Color( value ) : null;
- colorizationColorBalance = RegGetDword( HKEY_CURRENT_USER, subKey, "ColorizationColorBalance" );
+ colorizationColorBalance = registryGetIntValue( subKey, "ColorizationColorBalance", -1 );
}
- private static int RegGetDword( HKEY hkey, String lpSubKey, String lpValue ) {
+ private static int registryGetIntValue( String key, String valueName, int defaultValue ) {
try {
- return Advapi32Util.registryGetIntValue( hkey, lpSubKey, lpValue );
+ return Advapi32Util.registryGetIntValue( HKEY_CURRENT_USER, key, valueName );
} catch( RuntimeException ex ) {
- return -1;
+ return defaultValue;
}
}
diff --git a/flatlaf-natives/flatlaf-natives-windows/build.gradle.kts b/flatlaf-natives/flatlaf-natives-windows/build.gradle.kts
new file mode 100644
index 00000000..0a495709
--- /dev/null
+++ b/flatlaf-natives/flatlaf-natives-windows/build.gradle.kts
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+plugins {
+ id( "dev.nokee.jni-library" ) version "0.4.0"
+ id( "dev.nokee.cpp-language" ) version "0.4.0"
+}
+
+library {
+ targetMachines.set( listOf( machines.windows.x86_64 ) )
+
+ variants.configureEach {
+ // depend on :flatlaf-core:compileJava because this task generates the JNI headers
+ tasks.named( "compileCpp" ) {
+ dependsOn( ":flatlaf-core:compileJava" )
+ }
+
+ sharedLibrary {
+ compileTasks.configureEach {
+ doFirst {
+ println( "Used Tool Chain:" )
+ println( " - ${toolChain.get()}" )
+ println( "Available Tool Chains:" )
+ toolChains.forEach {
+ println( " - $it" )
+ }
+
+ // copy needed JNI headers
+ copy {
+ from( project( ":flatlaf-core" ).buildDir.resolve( "generated/jni-headers" ) )
+ into( "src/main/headers" )
+ include(
+ "com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder.h",
+ "com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc.h"
+ )
+ filter(
+ "eol" to org.apache.tools.ant.filters.FixCrLfFilter.CrLf.newInstance( "lf" )
+ )
+ }
+ }
+
+ compilerArgs.addAll( toolChain.map {
+ when( it ) {
+ is Gcc, is Clang -> listOf( "-O2" )
+ is VisualCpp -> listOf( "/O2" )
+ else -> emptyList()
+ }
+ } )
+ }
+
+ linkTask.configure {
+ val nativesDir = project( ":flatlaf-core" ).projectDir.resolve( "src/main/resources/com/formdev/flatlaf/natives" )
+ val libraryName = "flatlaf-windows-x86_64.dll"
+
+ outputs.file( "$nativesDir/$libraryName" )
+
+ val jawt = "${org.gradle.internal.jvm.Jvm.current().javaHome}/lib/jawt"
+ linkerArgs.addAll( toolChain.map {
+ when( it ) {
+ is Gcc, is Clang -> listOf( "-l${jawt}", "-lUser32", "-lshell32", "-lAdvAPI32" )
+ is VisualCpp -> listOf( "${jawt}.lib", "User32.lib", "shell32.lib", "AdvAPI32.lib" )
+ else -> emptyList()
+ }
+ } )
+
+ doLast {
+ // copy shared library to flatlaf-core resources
+ copy {
+ from( linkedFile )
+ into( nativesDir )
+ rename( "flatlaf-natives-windows.dll", libraryName )
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.cpp b/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.cpp
new file mode 100644
index 00000000..1f77c5e3
--- /dev/null
+++ b/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.cpp
@@ -0,0 +1,399 @@
+/*
+ * 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.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include