mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-13 07:17:13 -06:00
Merge pull request #267 from native-window-decorations
Native window decorations for Windows 10 (using JNI)
This commit is contained in:
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -15,8 +15,11 @@
|
|||||||
# BINARY FILES:
|
# BINARY FILES:
|
||||||
# Disable line ending normalize on checkin.
|
# Disable line ending normalize on checkin.
|
||||||
|
|
||||||
|
*.dll binary
|
||||||
|
*.dylib binary
|
||||||
*.gif binary
|
*.gif binary
|
||||||
*.jar binary
|
*.jar binary
|
||||||
*.png binary
|
*.png binary
|
||||||
*.sketch binary
|
*.sketch binary
|
||||||
|
*.so binary
|
||||||
*.zip binary
|
*.zip binary
|
||||||
|
|||||||
56
.github/workflows/natives.yml
vendored
Normal file
56
.github/workflows/natives.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
||||||
|
|
||||||
|
name: Native Libraries
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
tags:
|
||||||
|
- '[0-9]*'
|
||||||
|
paths:
|
||||||
|
- '**.cpp'
|
||||||
|
- '**.h'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '**.cpp'
|
||||||
|
- '**.h'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Windows:
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: gradle/wrapper-validation-action@v1
|
||||||
|
|
||||||
|
- name: Setup Java 1.8
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 1.8
|
||||||
|
|
||||||
|
- name: Cache Gradle wrapper
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/.gradle/wrapper
|
||||||
|
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
|
||||||
|
|
||||||
|
- name: Cache Gradle cache
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.gradle/caches
|
||||||
|
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
|
||||||
|
restore-keys: ${{ runner.os }}-gradle
|
||||||
|
|
||||||
|
- name: Build with Gradle
|
||||||
|
run: ./gradlew :flatlaf-natives-windows:build
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: FlatLaf-natives-windows-build-artifacts
|
||||||
|
path: |
|
||||||
|
flatlaf-natives/flatlaf-natives-windows/build
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -9,3 +9,5 @@ out/
|
|||||||
*.iml
|
*.iml
|
||||||
*.ipr
|
*.ipr
|
||||||
*.iws
|
*.iws
|
||||||
|
.vs/
|
||||||
|
.vscode/
|
||||||
|
|||||||
@@ -3,6 +3,13 @@ FlatLaf Change Log
|
|||||||
|
|
||||||
## 1.1-SNAPSHOT
|
## 1.1-SNAPSHOT
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Native window decorations for Windows 10 enables dark frame/dialog title bar
|
||||||
|
and embedded menu bar with all JREs, while still having native Windows 10
|
||||||
|
border drop shadows, resize behavior, window snapping and system window menu.
|
||||||
|
(PR #267)
|
||||||
|
|
||||||
#### Fixed bugs
|
#### Fixed bugs
|
||||||
|
|
||||||
- IntelliJ Themes: Fixed text color of CheckBoxMenuItem and RadioButtonMenuItem
|
- IntelliJ Themes: Fixed text color of CheckBoxMenuItem and RadioButtonMenuItem
|
||||||
|
|||||||
@@ -27,6 +27,16 @@ java {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
|
compileJava {
|
||||||
|
// generate JNI headers
|
||||||
|
options.headerOutputDirectory.set( buildDir.resolve( "generated/jni-headers" ) )
|
||||||
|
}
|
||||||
|
|
||||||
|
processResources {
|
||||||
|
// build native libraries
|
||||||
|
dependsOn( ":flatlaf-natives-windows:assemble" )
|
||||||
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
archiveBaseName.set( "flatlaf" )
|
archiveBaseName.set( "flatlaf" )
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ import javax.swing.ImageIcon;
|
|||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
import javax.swing.JRootPane;
|
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.PopupFactory;
|
import javax.swing.PopupFactory;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
@@ -90,9 +89,6 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
private Consumer<UIDefaults> postInitialization;
|
private Consumer<UIDefaults> postInitialization;
|
||||||
|
|
||||||
private Boolean oldFrameWindowDecorated;
|
|
||||||
private Boolean oldDialogWindowDecorated;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the application look and feel to the given LaF
|
* Sets the application look and feel to the given LaF
|
||||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||||
@@ -144,35 +140,20 @@ public abstract class FlatLaf
|
|||||||
* Returns whether FlatLaf supports custom window decorations.
|
* Returns whether FlatLaf supports custom window decorations.
|
||||||
* This depends on the operating system and on the used Java runtime.
|
* This depends on the operating system and on the used Java runtime.
|
||||||
* <p>
|
* <p>
|
||||||
* To use custom window decorations in your application, enable them with
|
|
||||||
* following code (before creating any frames or dialogs).
|
|
||||||
* <pre>
|
|
||||||
* JFrame.setDefaultLookAndFeelDecorated( true );
|
|
||||||
* JDialog.setDefaultLookAndFeelDecorated( true );
|
|
||||||
* </pre>
|
|
||||||
* <p>
|
|
||||||
* 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>
|
|
||||||
* This method returns {@code true} on Windows 10 (see exception below), {@code false} otherwise.
|
* This method returns {@code true} on Windows 10 (see exception below), {@code false} otherwise.
|
||||||
* <p>
|
* <p>
|
||||||
* Returns also {@code false} on Windows 10 if:
|
* Returns also {@code false} on Windows 10 if:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>FlatLaf native window border support is available (requires {@code flatlaf-natives-jna.jar})</li>
|
* <li>FlatLaf native window border support is available (requires Windows 10 64-bit)</li>
|
||||||
* <li>running in
|
* <li>running in
|
||||||
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime 11 (or later)</a>
|
* <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>)
|
* (<a href="https://github.com/JetBrains/JetBrainsRuntime">source code on github</a>)
|
||||||
* and JBR supports custom window decorations
|
* and JBR supports custom window decorations
|
||||||
* </li>
|
* </li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* In this case, custom decorations are enabled by the root pane
|
* In this cases, custom decorations are enabled by the root pane.
|
||||||
* if {@link JFrame#isDefaultLookAndFeelDecorated()} or
|
* Usage of {@link JFrame#setDefaultLookAndFeelDecorated(boolean)} or
|
||||||
* {@link JDialog#isDefaultLookAndFeelDecorated()} return {@code true}.
|
* {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean getSupportsWindowDecorations() {
|
public boolean getSupportsWindowDecorations() {
|
||||||
@@ -276,16 +257,6 @@ public abstract class FlatLaf
|
|||||||
String.format( "a, address { color: #%06x; }", linkColor.getRGB() & 0xffffff ) );
|
String.format( "a, address { color: #%06x; }", linkColor.getRGB() & 0xffffff ) );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// enable/disable window decorations, but only if system property is either
|
|
||||||
// "true" or "false"; in other cases it is not changed
|
|
||||||
Boolean useWindowDecorations = FlatSystemProperties.getBooleanStrict( FlatSystemProperties.USE_WINDOW_DECORATIONS, null );
|
|
||||||
if( useWindowDecorations != null ) {
|
|
||||||
oldFrameWindowDecorated = JFrame.isDefaultLookAndFeelDecorated();
|
|
||||||
oldDialogWindowDecorated = JDialog.isDefaultLookAndFeelDecorated();
|
|
||||||
JFrame.setDefaultLookAndFeelDecorated( useWindowDecorations );
|
|
||||||
JDialog.setDefaultLookAndFeelDecorated( useWindowDecorations );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -318,14 +289,6 @@ public abstract class FlatLaf
|
|||||||
new HTMLEditorKit().getStyleSheet().addRule( "a, address { color: blue; }" );
|
new HTMLEditorKit().getStyleSheet().addRule( "a, address { color: blue; }" );
|
||||||
postInitialization = null;
|
postInitialization = null;
|
||||||
|
|
||||||
// restore enable/disable window decorations
|
|
||||||
if( oldFrameWindowDecorated != null ) {
|
|
||||||
JFrame.setDefaultLookAndFeelDecorated( oldFrameWindowDecorated );
|
|
||||||
JDialog.setDefaultLookAndFeelDecorated( oldDialogWindowDecorated );
|
|
||||||
oldFrameWindowDecorated = null;
|
|
||||||
oldDialogWindowDecorated = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
super.uninitialize();
|
super.uninitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,6 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf;
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
import javax.swing.JDialog;
|
|
||||||
import javax.swing.JFrame;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines/documents own system properties used in FlatLaf.
|
* Defines/documents own system properties used in FlatLaf.
|
||||||
*
|
*
|
||||||
@@ -57,38 +54,21 @@ public interface FlatSystemProperties
|
|||||||
*/
|
*/
|
||||||
String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont";
|
String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont";
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies whether custom look and feel window decorations should be used
|
|
||||||
* when creating {@code JFrame} or {@code JDialog}.
|
|
||||||
* <p>
|
|
||||||
* If this system property is set, FlatLaf invokes {@link JFrame#setDefaultLookAndFeelDecorated(boolean)}
|
|
||||||
* and {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} on LaF initialization.
|
|
||||||
* <p>
|
|
||||||
* (requires Window 10)
|
|
||||||
* <p>
|
|
||||||
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
|
||||||
* <strong>Default</strong> none
|
|
||||||
*/
|
|
||||||
String USE_WINDOW_DECORATIONS = "flatlaf.useWindowDecorations";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether FlatLaf native window decorations should be used
|
* Specifies whether FlatLaf native window decorations should be used
|
||||||
* when creating {@code JFrame} or {@code JDialog}.
|
* when creating {@code JFrame} or {@code JDialog}.
|
||||||
* Requires that {@code flatlaf-natives-jna.jar} is on classpath/modulepath.
|
|
||||||
* <p>
|
* <p>
|
||||||
* Setting this to {@code true} forces using FlatLaf native window decorations
|
* Setting this to {@code true} forces using FlatLaf native window decorations
|
||||||
* even if they are not enabled by the application.
|
* even if they are not enabled by the application.
|
||||||
* <p>
|
* <p>
|
||||||
* Setting this to {@code false} disables using FlatLaf native window decorations.
|
* Setting this to {@code false} disables using FlatLaf native window decorations.
|
||||||
* <p>
|
* <p>
|
||||||
* (requires Window 10)
|
* (requires Window 10 64-bit)
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
* <strong>Default</strong> none
|
* <strong>Default</strong> none
|
||||||
*
|
|
||||||
* @since 1.1
|
|
||||||
*/
|
*/
|
||||||
String USE_NATIVE_WINDOW_DECORATIONS = "flatlaf.useNativeWindowDecorations";
|
String USE_WINDOW_DECORATIONS = "flatlaf.useWindowDecorations";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether JetBrains Runtime custom window decorations should be used
|
* Specifies whether JetBrains Runtime custom window decorations should be used
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import java.awt.Color;
|
|||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
@@ -70,7 +69,7 @@ public class FlatNativeWindowBorder
|
|||||||
// It could be also be a window that is currently hidden, but may be shown later.
|
// It could be also be a window that is currently hidden, but may be shown later.
|
||||||
Window window = SwingUtilities.windowForComponent( rootPane );
|
Window window = SwingUtilities.windowForComponent( rootPane );
|
||||||
if( window != null && window.isDisplayable() ) {
|
if( window != null && window.isDisplayable() ) {
|
||||||
install( window, FlatSystemProperties.USE_NATIVE_WINDOW_DECORATIONS );
|
install( window, FlatSystemProperties.USE_WINDOW_DECORATIONS );
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +79,7 @@ public class FlatNativeWindowBorder
|
|||||||
PropertyChangeListener ancestorListener = e -> {
|
PropertyChangeListener ancestorListener = e -> {
|
||||||
Object newValue = e.getNewValue();
|
Object newValue = e.getNewValue();
|
||||||
if( newValue instanceof Window )
|
if( newValue instanceof Window )
|
||||||
install( (Window) newValue, FlatSystemProperties.USE_NATIVE_WINDOW_DECORATIONS );
|
install( (Window) newValue, FlatSystemProperties.USE_WINDOW_DECORATIONS );
|
||||||
else if( newValue == null && e.getOldValue() instanceof Window )
|
else if( newValue == null && e.getOldValue() instanceof Window )
|
||||||
uninstall( (Window) e.getOldValue() );
|
uninstall( (Window) e.getOldValue() );
|
||||||
};
|
};
|
||||||
@@ -102,7 +101,8 @@ public class FlatNativeWindowBorder
|
|||||||
// do not enable native window border if JFrame should use system window decorations
|
// do not enable native window border if JFrame should use system window decorations
|
||||||
// and if not forced to use FlatLaf/JBR native window decorations
|
// and if not forced to use FlatLaf/JBR native window decorations
|
||||||
if( !JFrame.isDefaultLookAndFeelDecorated() &&
|
if( !JFrame.isDefaultLookAndFeelDecorated() &&
|
||||||
!FlatSystemProperties.getBoolean( systemPropertyKey, false ))
|
!UIManager.getBoolean( "TitlePane.useWindowDecorations" ) &&
|
||||||
|
!FlatSystemProperties.getBoolean( systemPropertyKey, false ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// do not enable native window border if frame is undecorated
|
// do not enable native window border if frame is undecorated
|
||||||
@@ -121,7 +121,8 @@ public class FlatNativeWindowBorder
|
|||||||
// do not enable native window border if JDialog should use system window decorations
|
// do not enable native window border if JDialog should use system window decorations
|
||||||
// and if not forced to use FlatLaf/JBR native window decorations
|
// and if not forced to use FlatLaf/JBR native window decorations
|
||||||
if( !JDialog.isDefaultLookAndFeelDecorated() &&
|
if( !JDialog.isDefaultLookAndFeelDecorated() &&
|
||||||
!FlatSystemProperties.getBoolean( systemPropertyKey, false ))
|
!UIManager.getBoolean( "TitlePane.useWindowDecorations" ) &&
|
||||||
|
!FlatSystemProperties.getBoolean( systemPropertyKey, false ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// do not enable native window border if dialog is undecorated
|
// do not enable native window border if dialog is undecorated
|
||||||
@@ -224,13 +225,17 @@ public class FlatNativeWindowBorder
|
|||||||
if( !SystemInfo.isWindows_10_orLater || !SystemInfo.isX86_64 )
|
if( !SystemInfo.isWindows_10_orLater || !SystemInfo.isX86_64 )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_NATIVE_WINDOW_DECORATIONS, true ) )
|
// check whether disabled via system property
|
||||||
|
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_WINDOW_DECORATIONS, true ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
/*
|
||||||
Class<?> cls = Class.forName( "com.formdev.flatlaf.natives.jna.windows.FlatWindowsNativeWindowBorder" );
|
Class<?> cls = Class.forName( "com.formdev.flatlaf.natives.jna.windows.FlatWindowsNativeWindowBorder" );
|
||||||
Method m = cls.getMethod( "getInstance" );
|
Method m = cls.getMethod( "getInstance" );
|
||||||
nativeProvider = (Provider) m.invoke( null );
|
nativeProvider = (Provider) m.invoke( null );
|
||||||
|
*/
|
||||||
|
nativeProvider = FlatWindowsNativeWindowBorder.getInstance();
|
||||||
|
|
||||||
supported = (nativeProvider != null);
|
supported = (nativeProvider != null);
|
||||||
} catch( Exception ex ) {
|
} catch( Exception ex ) {
|
||||||
|
|||||||
@@ -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.
|
||||||
|
* <p>
|
||||||
|
* 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<Window, WndProc> 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 <jdk>/bin/java.exe.
|
||||||
|
// When using <jdk>/jre/bin/java.exe, it is found.
|
||||||
|
// jawt.dll is located in <jdk>/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<Rectangle> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -165,6 +165,7 @@ public class JBRCustomDecorations
|
|||||||
if( !SystemInfo.isJetBrainsJVM_11_orLater || !SystemInfo.isWindows_10_orLater )
|
if( !SystemInfo.isJetBrainsJVM_11_orLater || !SystemInfo.isWindows_10_orLater )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// check whether disabled via system property
|
||||||
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, true ) )
|
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, true ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,193 @@
|
|||||||
|
/*
|
||||||
|
* 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.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
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.
|
||||||
|
* <p>
|
||||||
|
* Copies native library to users temporary folder before loading it.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class NativeLibrary
|
||||||
|
{
|
||||||
|
private static final String DELETE_SUFFIX = ".delete";
|
||||||
|
private static boolean deletedTemporary;
|
||||||
|
|
||||||
|
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.
|
||||||
|
* <p>
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
File tempFile = null;
|
||||||
|
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 = createTempFile( libraryName );
|
||||||
|
tempFile = tempPath.toFile();
|
||||||
|
|
||||||
|
// copy library to temporary file
|
||||||
|
try( InputStream in = libraryUrl.openStream() ) {
|
||||||
|
Files.copy( in, tempPath, StandardCopyOption.REPLACE_EXISTING );
|
||||||
|
}
|
||||||
|
|
||||||
|
// load library
|
||||||
|
System.load( tempFile.getCanonicalPath() );
|
||||||
|
|
||||||
|
// delete library
|
||||||
|
deleteOrMarkForDeletion( tempFile );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch( Throwable ex ) {
|
||||||
|
log( null, ex );
|
||||||
|
|
||||||
|
if( tempFile != null )
|
||||||
|
deleteOrMarkForDeletion( tempFile );
|
||||||
|
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 void log( String msg, Throwable thrown ) {
|
||||||
|
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, msg, thrown );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path createTempFile( String libraryName ) throws IOException {
|
||||||
|
int sep = libraryName.lastIndexOf( '/' );
|
||||||
|
String name = (sep >= 0) ? libraryName.substring( sep + 1 ) : libraryName;
|
||||||
|
|
||||||
|
int dot = name.lastIndexOf( '.' );
|
||||||
|
String prefix = ((dot >= 0) ? name.substring( 0, dot ) : name) + '-';
|
||||||
|
String suffix = (dot >= 0) ? name.substring( dot ) : "";
|
||||||
|
|
||||||
|
Path tempDir = getTempDir();
|
||||||
|
if( tempDir != null ) {
|
||||||
|
deleteTemporaryFiles( tempDir );
|
||||||
|
|
||||||
|
return Files.createTempFile( tempDir, prefix, suffix );
|
||||||
|
} else
|
||||||
|
return Files.createTempFile( prefix, suffix );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path getTempDir() throws IOException {
|
||||||
|
if( SystemInfo.isWindows ) {
|
||||||
|
// On Windows, where File.delete() and File.deleteOnExit() does not work
|
||||||
|
// for loaded native libraries, they will be deleted on next application startup.
|
||||||
|
// The default temporary directory may contain hundreds or thousands of files.
|
||||||
|
// To make searching for "marked for deletion" files as fast as possible,
|
||||||
|
// use a sub directory that contains only our temporary native libraries.
|
||||||
|
Path tempDir = Paths.get( System.getProperty( "java.io.tmpdir" ) + "/flatlaf.temp" );
|
||||||
|
Files.createDirectories( tempDir );
|
||||||
|
return tempDir;
|
||||||
|
} else
|
||||||
|
return null; // use standard temporary directory
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void deleteTemporaryFiles( Path tempDir ) {
|
||||||
|
if( deletedTemporary )
|
||||||
|
return;
|
||||||
|
deletedTemporary = true;
|
||||||
|
|
||||||
|
File[] markerFiles = tempDir.toFile().listFiles( (dir, name) -> name.endsWith( DELETE_SUFFIX ) );
|
||||||
|
if( markerFiles == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
for( File markerFile : markerFiles ) {
|
||||||
|
File toDeleteFile = new File( markerFile.getParent(), StringUtils.removeTrailing( markerFile.getName(), DELETE_SUFFIX ) );
|
||||||
|
if( !toDeleteFile.exists() || toDeleteFile.delete() )
|
||||||
|
markerFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void deleteOrMarkForDeletion( File file ) {
|
||||||
|
// try to delete the native library
|
||||||
|
if( file.delete() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// not possible to delete on Windows because native library file is locked
|
||||||
|
// --> create "to delete" marker file (used at next startup)
|
||||||
|
try {
|
||||||
|
File markFile = new File( file.getParent(), file.getName() + DELETE_SUFFIX );
|
||||||
|
markFile.createNewFile();
|
||||||
|
} catch( IOException ex2 ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -685,6 +685,7 @@ TitledBorder.border = 1,1,1,1,$Separator.foreground
|
|||||||
|
|
||||||
#---- TitlePane ----
|
#---- TitlePane ----
|
||||||
|
|
||||||
|
TitlePane.useWindowDecorations = true
|
||||||
TitlePane.menuBarEmbedded = true
|
TitlePane.menuBarEmbedded = true
|
||||||
TitlePane.iconSize = 16,16
|
TitlePane.iconSize = 16,16
|
||||||
TitlePane.iconMargins = 3,8,3,0
|
TitlePane.iconMargins = 3,8,3,0
|
||||||
|
|||||||
Binary file not shown.
@@ -27,19 +27,19 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation( project( ":flatlaf-core" ) )
|
implementation( project( ":flatlaf-core" ) )
|
||||||
implementation( project( ":flatlaf-natives-jna" ) )
|
|
||||||
implementation( project( ":flatlaf-extras" ) )
|
implementation( project( ":flatlaf-extras" ) )
|
||||||
implementation( project( ":flatlaf-intellij-themes" ) )
|
implementation( project( ":flatlaf-intellij-themes" ) )
|
||||||
implementation( "com.miglayout:miglayout-swing:5.3-SNAPSHOT" )
|
implementation( "com.miglayout:miglayout-swing:5.3-SNAPSHOT" )
|
||||||
implementation( "com.jgoodies:jgoodies-forms:1.9.0" )
|
implementation( "com.jgoodies:jgoodies-forms:1.9.0" )
|
||||||
|
// implementation( project( ":flatlaf-natives-jna" ) )
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
jar {
|
jar {
|
||||||
dependsOn( ":flatlaf-core:jar" )
|
dependsOn( ":flatlaf-core:jar" )
|
||||||
dependsOn( ":flatlaf-natives-jna:jar" )
|
|
||||||
dependsOn( ":flatlaf-extras:jar" )
|
dependsOn( ":flatlaf-extras:jar" )
|
||||||
dependsOn( ":flatlaf-intellij-themes:jar" )
|
dependsOn( ":flatlaf-intellij-themes:jar" )
|
||||||
|
// dependsOn( ":flatlaf-natives-jna:jar" )
|
||||||
|
|
||||||
manifest {
|
manifest {
|
||||||
attributes( "Main-Class" to "com.formdev.flatlaf.demo.FlatLafDemo" )
|
attributes( "Main-Class" to "com.formdev.flatlaf.demo.FlatLafDemo" )
|
||||||
|
|||||||
@@ -155,8 +155,7 @@ class DemoFrame
|
|||||||
menuBarEmbeddedCheckBoxMenuItem.setEnabled( windowDecorations );
|
menuBarEmbeddedCheckBoxMenuItem.setEnabled( windowDecorations );
|
||||||
|
|
||||||
// enable/disable window decoration for later created frames/dialogs
|
// enable/disable window decoration for later created frames/dialogs
|
||||||
JFrame.setDefaultLookAndFeelDecorated( windowDecorations );
|
UIManager.put( "TitlePane.useWindowDecorations", windowDecorations );
|
||||||
JDialog.setDefaultLookAndFeelDecorated( windowDecorations );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void menuBarEmbeddedChanged() {
|
private void menuBarEmbeddedChanged() {
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
package com.formdev.flatlaf.demo;
|
package com.formdev.flatlaf.demo;
|
||||||
|
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import javax.swing.JDialog;
|
|
||||||
import javax.swing.JFrame;
|
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
import com.formdev.flatlaf.extras.FlatInspector;
|
import com.formdev.flatlaf.extras.FlatInspector;
|
||||||
@@ -46,10 +44,6 @@ public class FlatLafDemo
|
|||||||
SwingUtilities.invokeLater( () -> {
|
SwingUtilities.invokeLater( () -> {
|
||||||
DemoPrefs.init( PREFS_ROOT_PATH );
|
DemoPrefs.init( PREFS_ROOT_PATH );
|
||||||
|
|
||||||
// enable window decorations
|
|
||||||
JFrame.setDefaultLookAndFeelDecorated( true );
|
|
||||||
JDialog.setDefaultLookAndFeelDecorated( true );
|
|
||||||
|
|
||||||
// application specific UI defaults
|
// application specific UI defaults
|
||||||
FlatLaf.registerCustomDefaultsSource( "com.formdev.flatlaf.demo" );
|
FlatLaf.registerCustomDefaultsSource( "com.formdev.flatlaf.demo" );
|
||||||
|
|
||||||
|
|||||||
5
flatlaf-natives/README.md
Normal file
5
flatlaf-natives/README.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
FlatLaf Native Libraries
|
||||||
|
========================
|
||||||
|
|
||||||
|
- [Windows 10 Native Library](flatlaf-natives-windows)
|
||||||
|
- [Natives using JNA](flatlaf-natives-jna) (for development only)
|
||||||
10
flatlaf-natives/flatlaf-natives-jna/README.md
Normal file
10
flatlaf-natives/flatlaf-natives-jna/README.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
FlatLaf Natives using JNA
|
||||||
|
=========================
|
||||||
|
|
||||||
|
This sub-project contains source code that uses
|
||||||
|
[JNA](https://github.com/java-native-access/jna) to access native operating
|
||||||
|
system API.
|
||||||
|
|
||||||
|
**Note:** Code in this sub-project is **not used** in FlatLaf libraries. It was
|
||||||
|
used to develop/test usage of some native operating system API in Java (with the
|
||||||
|
help of JNA) and was then converted to C++.
|
||||||
@@ -207,20 +207,20 @@ public class FlatWindowsNativeWindowBorder
|
|||||||
|
|
||||||
String subKey = "SOFTWARE\\Microsoft\\Windows\\DWM";
|
String subKey = "SOFTWARE\\Microsoft\\Windows\\DWM";
|
||||||
|
|
||||||
int value = RegGetDword( HKEY_CURRENT_USER, subKey, "ColorPrevalence" );
|
int value = registryGetIntValue( subKey, "ColorPrevalence", -1 );
|
||||||
colorizationColorAffectsBorders = (value > 0);
|
colorizationColorAffectsBorders = (value > 0);
|
||||||
|
|
||||||
value = RegGetDword( HKEY_CURRENT_USER, subKey, "ColorizationColor" );
|
value = registryGetIntValue( subKey, "ColorizationColor", -1 );
|
||||||
colorizationColor = (value != -1) ? new Color( value ) : null;
|
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 {
|
try {
|
||||||
return Advapi32Util.registryGetIntValue( hkey, lpSubKey, lpValue );
|
return Advapi32Util.registryGetIntValue( HKEY_CURRENT_USER, key, valueName );
|
||||||
} catch( RuntimeException ex ) {
|
} catch( RuntimeException ex ) {
|
||||||
return -1;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
17
flatlaf-natives/flatlaf-natives-windows/README.md
Normal file
17
flatlaf-natives/flatlaf-natives-windows/README.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
FlatLaf Windows 10 Native Library
|
||||||
|
=================================
|
||||||
|
|
||||||
|
This sub-project contains the source code for the FlatLaf Windows 10 native
|
||||||
|
library (DLL).
|
||||||
|
|
||||||
|
The native library can be built only on Windows and requires a C++ compiler.
|
||||||
|
Tested only with Microsoft Visual C++ 2019 (comes with Visual Studio 2019).
|
||||||
|
|
||||||
|
To be able to build FlatLaf on any platform, and without C++ compiler, the
|
||||||
|
pre-built DLL is checked into Git at
|
||||||
|
`flatlaf-core/src/main/resources/com/formdev/flatlaf/natives/flatlaf-windows-x86_64.dll`.
|
||||||
|
|
||||||
|
This DLL was built on a GitHub server with the help of GitHub Actions. See:
|
||||||
|
[Native Libraries](https://github.com/JFormDesigner/FlatLaf/actions/workflows/natives.yml)
|
||||||
|
workflow. Then the produced Artifacts ZIP was downloaded and the DLL checked
|
||||||
|
into Git.
|
||||||
98
flatlaf-natives/flatlaf-natives-windows/build.gradle.kts
Normal file
98
flatlaf-natives/flatlaf-natives-windows/build.gradle.kts
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* 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 {
|
||||||
|
onlyIf { isBuildable }
|
||||||
|
|
||||||
|
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<org.apache.tools.ant.filters.FixCrLfFilter>(
|
||||||
|
"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", "/Zl", "/GS-" )
|
||||||
|
else -> emptyList()
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
}
|
||||||
|
|
||||||
|
linkTask.configure {
|
||||||
|
onlyIf { isBuildable }
|
||||||
|
|
||||||
|
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", "-lKernel32" )
|
||||||
|
is VisualCpp -> listOf( "${jawt}.lib", "User32.lib", "shell32.lib", "AdvAPI32.lib", "Kernel32.lib", "/NODEFAULTLIB" )
|
||||||
|
else -> emptyList()
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
|
||||||
|
doLast {
|
||||||
|
// copy shared library to flatlaf-core resources
|
||||||
|
copy {
|
||||||
|
from( linkedFile )
|
||||||
|
into( nativesDir )
|
||||||
|
rename( "flatlaf-natives-windows.dll", libraryName )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named( "jar" ) {
|
||||||
|
onlyIf { false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,409 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// avoid inlining of printf()
|
||||||
|
#define _NO_CRT_STDIO_INLINE
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <windowsx.h>
|
||||||
|
#include <shellapi.h>
|
||||||
|
#include <jawt.h>
|
||||||
|
#include <jawt_md.h>
|
||||||
|
#include "FlatWndProc.h"
|
||||||
|
#include "com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---- JNI methods ------------------------------------------------------------
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jlong JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_00024WndProc_installImpl
|
||||||
|
( JNIEnv *env, jobject obj, jobject window )
|
||||||
|
{
|
||||||
|
return reinterpret_cast<jlong>( FlatWndProc::install( env, obj, window ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT void JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_00024WndProc_uninstallImpl
|
||||||
|
( JNIEnv* env, jobject obj, jlong hwnd )
|
||||||
|
{
|
||||||
|
FlatWndProc::uninstall( env, obj, reinterpret_cast<HWND>( hwnd ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatWndProc fields -----------------------------------------------
|
||||||
|
|
||||||
|
int FlatWndProc::initialized = 0;
|
||||||
|
jmethodID FlatWndProc::onNcHitTestMID;
|
||||||
|
jmethodID FlatWndProc::isFullscreenMID;
|
||||||
|
jmethodID FlatWndProc::fireStateChangedLaterOnceMID;
|
||||||
|
|
||||||
|
HWNDMap* FlatWndProc::hwndMap;
|
||||||
|
|
||||||
|
//---- class FlatWndProc methods ----------------------------------------------
|
||||||
|
|
||||||
|
FlatWndProc::FlatWndProc() {
|
||||||
|
jvm = NULL;
|
||||||
|
env = NULL;
|
||||||
|
obj = NULL;
|
||||||
|
hwnd = NULL;
|
||||||
|
defaultWndProc = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
HWND FlatWndProc::install( JNIEnv *env, jobject obj, jobject window ) {
|
||||||
|
initIDs( env, obj );
|
||||||
|
|
||||||
|
if( initialized < 0 )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// create HWND map
|
||||||
|
if( hwndMap == NULL )
|
||||||
|
hwndMap = new HWNDMap();
|
||||||
|
|
||||||
|
// get window handle
|
||||||
|
HWND hwnd = getWindowHandle( env, window );
|
||||||
|
if( hwnd == NULL || hwndMap->get( hwnd ) != NULL )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
FlatWndProc* fwp = new FlatWndProc();
|
||||||
|
env->GetJavaVM( &fwp->jvm );
|
||||||
|
fwp->obj = env->NewGlobalRef( obj );
|
||||||
|
fwp->hwnd = hwnd;
|
||||||
|
hwndMap->put( hwnd, fwp );
|
||||||
|
|
||||||
|
// replace window procedure
|
||||||
|
fwp->defaultWndProc = reinterpret_cast<WNDPROC>(
|
||||||
|
::SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG_PTR) FlatWndProc::StaticWindowProc ) );
|
||||||
|
|
||||||
|
// remove the OS window title bar
|
||||||
|
fwp->updateFrame();
|
||||||
|
|
||||||
|
return hwnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatWndProc::uninstall( JNIEnv *env, jobject obj, HWND hwnd ) {
|
||||||
|
if( hwnd == NULL )
|
||||||
|
return;
|
||||||
|
|
||||||
|
FlatWndProc* fwp = (FlatWndProc*) hwndMap->get( hwnd );
|
||||||
|
if( fwp == NULL )
|
||||||
|
return;
|
||||||
|
|
||||||
|
hwndMap->remove( hwnd );
|
||||||
|
|
||||||
|
// restore original window procedure
|
||||||
|
::SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG_PTR) fwp->defaultWndProc );
|
||||||
|
|
||||||
|
// show the OS window title bar
|
||||||
|
fwp->updateFrame();
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
env->DeleteGlobalRef( fwp->obj );
|
||||||
|
delete fwp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatWndProc::initIDs( JNIEnv *env, jobject obj ) {
|
||||||
|
if( initialized )
|
||||||
|
return;
|
||||||
|
|
||||||
|
initialized = -1;
|
||||||
|
|
||||||
|
jclass cls = env->GetObjectClass( obj );
|
||||||
|
onNcHitTestMID = env->GetMethodID( cls, "onNcHitTest", "(IIZ)I" );
|
||||||
|
isFullscreenMID = env->GetMethodID( cls, "isFullscreen", "()Z" );
|
||||||
|
fireStateChangedLaterOnceMID = env->GetMethodID( cls, "fireStateChangedLaterOnce", "()V" );
|
||||||
|
|
||||||
|
// check whether all IDs were found
|
||||||
|
if( onNcHitTestMID != NULL &&
|
||||||
|
isFullscreenMID != NULL &&
|
||||||
|
fireStateChangedLaterOnceMID != NULL )
|
||||||
|
initialized = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatWndProc::updateFrame() {
|
||||||
|
// this sends WM_NCCALCSIZE and removes/shows the window title bar
|
||||||
|
::SetWindowPos( hwnd, hwnd, 0, 0, 0, 0,
|
||||||
|
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER );
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CALLBACK FlatWndProc::StaticWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
|
||||||
|
FlatWndProc* fwp = (FlatWndProc*) hwndMap->get( hwnd );
|
||||||
|
return fwp->WindowProc( hwnd, uMsg, wParam, lParam );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOTE: This method is invoked on the AWT-Windows thread (not the AWT-EventQueue thread).
|
||||||
|
*/
|
||||||
|
LRESULT CALLBACK FlatWndProc::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
|
||||||
|
switch( uMsg ) {
|
||||||
|
case WM_NCCALCSIZE:
|
||||||
|
return WmNcCalcSize( hwnd, uMsg, wParam, lParam );
|
||||||
|
|
||||||
|
case WM_NCHITTEST:
|
||||||
|
return WmNcHitTest( hwnd, uMsg, wParam, lParam );
|
||||||
|
|
||||||
|
case WM_NCRBUTTONUP:
|
||||||
|
if( wParam == HTCAPTION || wParam == HTSYSMENU )
|
||||||
|
openSystemMenu( hwnd, GET_X_LPARAM( lParam ), GET_Y_LPARAM( lParam ) );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WM_DWMCOLORIZATIONCOLORCHANGED:
|
||||||
|
fireStateChangedLaterOnce();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WM_DESTROY:
|
||||||
|
return WmDestroy( hwnd, uMsg, wParam, lParam );
|
||||||
|
}
|
||||||
|
|
||||||
|
return ::CallWindowProc( defaultWndProc, hwnd, uMsg, wParam, lParam );
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handle WM_DESTROY
|
||||||
|
*
|
||||||
|
* https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-destroy
|
||||||
|
*/
|
||||||
|
LRESULT FlatWndProc::WmDestroy( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam ) {
|
||||||
|
// restore original window procedure
|
||||||
|
::SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG_PTR) defaultWndProc );
|
||||||
|
|
||||||
|
WNDPROC defaultWndProc2 = defaultWndProc;
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
getEnv()->DeleteGlobalRef( obj );
|
||||||
|
hwndMap->remove( hwnd );
|
||||||
|
delete this;
|
||||||
|
|
||||||
|
// call original AWT window procedure because it may fire window closed event in AwtWindow::WmDestroy()
|
||||||
|
return ::CallWindowProc( defaultWndProc2, hwnd, uMsg, wParam, lParam );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle WM_NCCALCSIZE
|
||||||
|
*
|
||||||
|
* https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-nccalcsize
|
||||||
|
*
|
||||||
|
* See also NonClientIslandWindow::_OnNcCalcSize() here:
|
||||||
|
* https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
|
||||||
|
*/
|
||||||
|
LRESULT FlatWndProc::WmNcCalcSize( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam ) {
|
||||||
|
if( wParam != TRUE )
|
||||||
|
return ::CallWindowProc( defaultWndProc, hwnd, uMsg, wParam, lParam );
|
||||||
|
|
||||||
|
NCCALCSIZE_PARAMS* params = reinterpret_cast<NCCALCSIZE_PARAMS*>( lParam );
|
||||||
|
|
||||||
|
// store the original top before the default window proc applies the default frame
|
||||||
|
int originalTop = params->rgrc[0].top;
|
||||||
|
|
||||||
|
// apply the default frame
|
||||||
|
LRESULT lResult = ::CallWindowProc( defaultWndProc, hwnd, uMsg, wParam, lParam );
|
||||||
|
if( lResult != 0 )
|
||||||
|
return lResult;
|
||||||
|
|
||||||
|
// re-apply the original top from before the size of the default frame was applied
|
||||||
|
params->rgrc[0].top = originalTop;
|
||||||
|
|
||||||
|
bool isMaximized = ::IsZoomed( hwnd );
|
||||||
|
if( isMaximized && !isFullscreen() ) {
|
||||||
|
// When a window is maximized, its size is actually a little bit more
|
||||||
|
// than the monitor's work area. The window is positioned and sized in
|
||||||
|
// such a way that the resize handles are outside of the monitor and
|
||||||
|
// then the window is clipped to the monitor so that the resize handle
|
||||||
|
// do not appear because you don't need them (because you can't resize
|
||||||
|
// a window when it's maximized unless you restore it).
|
||||||
|
params->rgrc[0].top += getResizeHandleHeight();
|
||||||
|
|
||||||
|
// check whether taskbar is in the autohide state
|
||||||
|
APPBARDATA autohide{ 0 };
|
||||||
|
autohide.cbSize = sizeof( autohide );
|
||||||
|
UINT state = (UINT) ::SHAppBarMessage( ABM_GETSTATE, &autohide );
|
||||||
|
if( (state & ABS_AUTOHIDE) != 0 ) {
|
||||||
|
// get monitor info
|
||||||
|
// (using MONITOR_DEFAULTTONEAREST finds right monitor when restoring from minimized)
|
||||||
|
HMONITOR hMonitor = ::MonitorFromWindow( hwnd, MONITOR_DEFAULTTONEAREST );
|
||||||
|
MONITORINFO monitorInfo{ 0 };
|
||||||
|
::GetMonitorInfo( hMonitor, &monitorInfo );
|
||||||
|
|
||||||
|
// If there's a taskbar on any side of the monitor, reduce our size
|
||||||
|
// a little bit on that edge.
|
||||||
|
if( hasAutohideTaskbar( ABE_TOP, monitorInfo.rcMonitor ) )
|
||||||
|
params->rgrc[0].top++;
|
||||||
|
if( hasAutohideTaskbar( ABE_BOTTOM, monitorInfo.rcMonitor ) )
|
||||||
|
params->rgrc[0].bottom--;
|
||||||
|
if( hasAutohideTaskbar( ABE_LEFT, monitorInfo.rcMonitor ) )
|
||||||
|
params->rgrc[0].left++;
|
||||||
|
if( hasAutohideTaskbar( ABE_RIGHT, monitorInfo.rcMonitor ) )
|
||||||
|
params->rgrc[0].right--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle WM_NCHITTEST
|
||||||
|
*
|
||||||
|
* https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-nchittest
|
||||||
|
*
|
||||||
|
* See also NonClientIslandWindow::_OnNcHitTest() here:
|
||||||
|
* https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
|
||||||
|
*/
|
||||||
|
LRESULT FlatWndProc::WmNcHitTest( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam ) {
|
||||||
|
// this will handle the left, right and bottom parts of the frame because we didn't change them
|
||||||
|
LRESULT lResult = ::CallWindowProc( defaultWndProc, hwnd, uMsg, wParam, lParam );
|
||||||
|
if( lResult != HTCLIENT )
|
||||||
|
return lResult;
|
||||||
|
|
||||||
|
// get window rectangle needed to convert mouse x/y from screen to window coordinates
|
||||||
|
RECT rcWindow;
|
||||||
|
::GetWindowRect( hwnd, &rcWindow );
|
||||||
|
|
||||||
|
// get mouse x/y in window coordinates
|
||||||
|
int x = GET_X_LPARAM( lParam ) - rcWindow.left;
|
||||||
|
int y = GET_Y_LPARAM( lParam ) - rcWindow.top;
|
||||||
|
|
||||||
|
int resizeBorderHeight = getResizeHandleHeight();
|
||||||
|
bool isOnResizeBorder = (y < resizeBorderHeight) &&
|
||||||
|
(::GetWindowLong( hwnd, GWL_STYLE ) & WS_THICKFRAME) != 0;
|
||||||
|
|
||||||
|
return onNcHitTest( x, y, isOnResizeBorder );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the height of the little space at the top of the window used to
|
||||||
|
* resize the window.
|
||||||
|
*
|
||||||
|
* See also NonClientIslandWindow::_GetResizeHandleHeight() here:
|
||||||
|
* https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
|
||||||
|
*/
|
||||||
|
int FlatWndProc::getResizeHandleHeight() {
|
||||||
|
int dpi = ::GetDpiForWindow( hwnd );
|
||||||
|
|
||||||
|
// there isn't a SM_CYPADDEDBORDER for the Y axis
|
||||||
|
return ::GetSystemMetricsForDpi( SM_CXPADDEDBORDER, dpi )
|
||||||
|
+ ::GetSystemMetricsForDpi( SM_CYSIZEFRAME, dpi );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether there is an autohide taskbar on the given edge.
|
||||||
|
*/
|
||||||
|
bool FlatWndProc::hasAutohideTaskbar( UINT edge, RECT rcMonitor ) {
|
||||||
|
APPBARDATA data{ 0 };
|
||||||
|
data.cbSize = sizeof( data );
|
||||||
|
data.uEdge = edge;
|
||||||
|
data.rc = rcMonitor;
|
||||||
|
HWND hTaskbar = (HWND) ::SHAppBarMessage( ABM_GETAUTOHIDEBAREX, &data );
|
||||||
|
return hTaskbar != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL FlatWndProc::isFullscreen() {
|
||||||
|
JNIEnv* env = getEnv();
|
||||||
|
if( env == NULL )
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return env->CallBooleanMethod( obj, isFullscreenMID );
|
||||||
|
}
|
||||||
|
|
||||||
|
int FlatWndProc::onNcHitTest( int x, int y, boolean isOnResizeBorder ) {
|
||||||
|
JNIEnv* env = getEnv();
|
||||||
|
if( env == NULL )
|
||||||
|
return isOnResizeBorder ? HTTOP : HTCLIENT;
|
||||||
|
|
||||||
|
return env->CallIntMethod( obj, onNcHitTestMID, (jint) x, (jint) y, (jboolean) isOnResizeBorder );
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatWndProc::fireStateChangedLaterOnce() {
|
||||||
|
JNIEnv* env = getEnv();
|
||||||
|
if( env == NULL )
|
||||||
|
return;
|
||||||
|
|
||||||
|
env->CallVoidMethod( obj, fireStateChangedLaterOnceMID );
|
||||||
|
}
|
||||||
|
|
||||||
|
// similar to JNU_GetEnv() in jni_util.c
|
||||||
|
JNIEnv* FlatWndProc::getEnv() {
|
||||||
|
if( env != NULL )
|
||||||
|
return env;
|
||||||
|
|
||||||
|
jvm->GetEnv( (void **) &env, JNI_VERSION_1_2 );
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the window's system menu.
|
||||||
|
* The system menu is the menu that opens when the user presses Alt+Space or
|
||||||
|
* right clicks on the title bar
|
||||||
|
*/
|
||||||
|
void FlatWndProc::openSystemMenu( HWND hwnd, int x, int y ) {
|
||||||
|
// get system menu
|
||||||
|
HMENU systemMenu = ::GetSystemMenu( hwnd, false );
|
||||||
|
|
||||||
|
// update system menu
|
||||||
|
LONG style = ::GetWindowLong( hwnd, GWL_STYLE );
|
||||||
|
bool isMaximized = ::IsZoomed( hwnd );
|
||||||
|
setMenuItemState( systemMenu, SC_RESTORE, isMaximized );
|
||||||
|
setMenuItemState( systemMenu, SC_MOVE, !isMaximized );
|
||||||
|
setMenuItemState( systemMenu, SC_SIZE, (style & WS_THICKFRAME) != 0 && !isMaximized );
|
||||||
|
setMenuItemState( systemMenu, SC_MINIMIZE, (style & WS_MINIMIZEBOX) != 0 );
|
||||||
|
setMenuItemState( systemMenu, SC_MAXIMIZE, (style & WS_MAXIMIZEBOX) != 0 && !isMaximized );
|
||||||
|
setMenuItemState( systemMenu, SC_CLOSE, true );
|
||||||
|
|
||||||
|
// make "Close" item the default to be consistent with the system menu shown
|
||||||
|
// when pressing Alt+Space
|
||||||
|
::SetMenuDefaultItem( systemMenu, SC_CLOSE, 0 );
|
||||||
|
|
||||||
|
// show system menu
|
||||||
|
int ret = ::TrackPopupMenu( systemMenu, TPM_RETURNCMD, x, y, 0, hwnd, nullptr );
|
||||||
|
if( ret != 0 )
|
||||||
|
::PostMessage( hwnd, WM_SYSCOMMAND, ret, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatWndProc::setMenuItemState( HMENU systemMenu, int item, bool enabled ) {
|
||||||
|
MENUITEMINFO mii{ 0 };
|
||||||
|
mii.cbSize = sizeof( mii );
|
||||||
|
mii.fMask = MIIM_STATE;
|
||||||
|
mii.fType = MFT_STRING;
|
||||||
|
mii.fState = enabled ? MF_ENABLED : MF_DISABLED;
|
||||||
|
::SetMenuItemInfo( systemMenu, item, FALSE, &mii );
|
||||||
|
}
|
||||||
|
|
||||||
|
HWND FlatWndProc::getWindowHandle( JNIEnv* env, jobject window ) {
|
||||||
|
JAWT awt;
|
||||||
|
awt.version = JAWT_VERSION_1_4;
|
||||||
|
if( !JAWT_GetAWT( env, &awt ) )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
jawt_DrawingSurface* ds = awt.GetDrawingSurface( env, window );
|
||||||
|
if( ds == NULL )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
jint lock = ds->Lock( ds );
|
||||||
|
if( (lock & JAWT_LOCK_ERROR) != 0 ) {
|
||||||
|
awt.FreeDrawingSurface( ds );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
JAWT_DrawingSurfaceInfo* dsi = ds->GetDrawingSurfaceInfo( ds );
|
||||||
|
JAWT_Win32DrawingSurfaceInfo* wdsi = (JAWT_Win32DrawingSurfaceInfo*) dsi->platformInfo;
|
||||||
|
|
||||||
|
HWND hwnd = wdsi->hwnd;
|
||||||
|
|
||||||
|
ds->FreeDrawingSurfaceInfo( dsi );
|
||||||
|
ds->Unlock( ds );
|
||||||
|
awt.FreeDrawingSurface( ds );
|
||||||
|
|
||||||
|
return hwnd;
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* 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 <windows.h>
|
||||||
|
#include "HWNDMap.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
class FlatWndProc
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static HWND install( JNIEnv *env, jobject obj, jobject window );
|
||||||
|
static void uninstall( JNIEnv *env, jobject obj, HWND hwnd );
|
||||||
|
|
||||||
|
private:
|
||||||
|
static int initialized;
|
||||||
|
static jmethodID onNcHitTestMID;
|
||||||
|
static jmethodID isFullscreenMID;
|
||||||
|
static jmethodID fireStateChangedLaterOnceMID;
|
||||||
|
|
||||||
|
static HWNDMap* hwndMap;
|
||||||
|
|
||||||
|
JavaVM* jvm;
|
||||||
|
JNIEnv* env; // attached to AWT-Windows/Win32 thread
|
||||||
|
jobject obj;
|
||||||
|
HWND hwnd;
|
||||||
|
WNDPROC defaultWndProc;
|
||||||
|
|
||||||
|
FlatWndProc();
|
||||||
|
static void initIDs( JNIEnv *env, jobject obj );
|
||||||
|
void updateFrame();
|
||||||
|
|
||||||
|
static LRESULT CALLBACK StaticWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
|
||||||
|
LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
|
||||||
|
LRESULT WmDestroy( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam );
|
||||||
|
LRESULT WmNcCalcSize( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam );
|
||||||
|
LRESULT WmNcHitTest( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam );
|
||||||
|
|
||||||
|
int getResizeHandleHeight();
|
||||||
|
bool hasAutohideTaskbar( UINT edge, RECT rcMonitor );
|
||||||
|
BOOL isFullscreen();
|
||||||
|
int onNcHitTest( int x, int y, boolean isOnResizeBorder );
|
||||||
|
void fireStateChangedLaterOnce();
|
||||||
|
JNIEnv* getEnv();
|
||||||
|
|
||||||
|
void openSystemMenu( HWND hwnd, int x, int y );
|
||||||
|
void setMenuItemState( HMENU systemMenu, int item, bool enabled );
|
||||||
|
|
||||||
|
static HWND getWindowHandle( JNIEnv* env, jobject window );
|
||||||
|
};
|
||||||
153
flatlaf-natives/flatlaf-natives-windows/src/main/cpp/HWNDMap.cpp
Normal file
153
flatlaf-natives/flatlaf-natives-windows/src/main/cpp/HWNDMap.cpp
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// avoid inlining of printf()
|
||||||
|
#define _NO_CRT_STDIO_INLINE
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "HWNDMap.h"
|
||||||
|
|
||||||
|
#define DEFAULT_CAPACITY 20
|
||||||
|
#define INCREASE_CAPACITY 10
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
|
||||||
|
class LOCK {
|
||||||
|
LPCRITICAL_SECTION lpCriticalSection;
|
||||||
|
|
||||||
|
public:
|
||||||
|
LOCK( LPCRITICAL_SECTION lpCriticalSection ) {
|
||||||
|
this->lpCriticalSection = lpCriticalSection;
|
||||||
|
::EnterCriticalSection( lpCriticalSection );
|
||||||
|
}
|
||||||
|
~LOCK() {
|
||||||
|
::LeaveCriticalSection( lpCriticalSection );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
HWNDMap::HWNDMap() {
|
||||||
|
size = 0;
|
||||||
|
capacity = DEFAULT_CAPACITY;
|
||||||
|
table = new Entry[capacity];
|
||||||
|
|
||||||
|
::InitializeCriticalSection( &criticalSection );
|
||||||
|
|
||||||
|
// dump( "<init>" );
|
||||||
|
}
|
||||||
|
|
||||||
|
LPVOID HWNDMap::get( HWND key ) {
|
||||||
|
LOCK lock( &criticalSection );
|
||||||
|
|
||||||
|
int index = binarySearch( key );
|
||||||
|
return (index >= 0) ? table[index].value : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HWNDMap::put( HWND key, LPVOID value ) {
|
||||||
|
LOCK lock( &criticalSection );
|
||||||
|
|
||||||
|
int index = binarySearch( key );
|
||||||
|
// printf( "put %p %p = %d --\n", key, value, index );
|
||||||
|
if( index >= 0 ) {
|
||||||
|
// key already in map --> replace
|
||||||
|
table[index].value = value;
|
||||||
|
} else {
|
||||||
|
// insert new key
|
||||||
|
ensureCapacity( size + 1 );
|
||||||
|
|
||||||
|
// make roor for new entry
|
||||||
|
index = -(index + 1);
|
||||||
|
for( int i = size - 1; i >= index; i-- )
|
||||||
|
table[i + 1] = table[i];
|
||||||
|
size++;
|
||||||
|
|
||||||
|
// insert entry
|
||||||
|
table[index].key = key;
|
||||||
|
table[index].value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// dump( "put" );
|
||||||
|
}
|
||||||
|
|
||||||
|
void HWNDMap::remove( HWND key ) {
|
||||||
|
LOCK lock( &criticalSection );
|
||||||
|
|
||||||
|
// search for key
|
||||||
|
int index = binarySearch( key );
|
||||||
|
// printf( "remove %p = %d --\n", key, index );
|
||||||
|
if( index < 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// remove entry
|
||||||
|
for( int i = index + 1; i < size; i++ )
|
||||||
|
table[i - 1] = table[i];
|
||||||
|
size--;
|
||||||
|
|
||||||
|
// dump( "remove" );
|
||||||
|
}
|
||||||
|
|
||||||
|
int HWNDMap::binarySearch( HWND key ) {
|
||||||
|
int low = 0;
|
||||||
|
int high = size - 1;
|
||||||
|
|
||||||
|
while( low <= high ) {
|
||||||
|
int mid = (low + high) >> 1;
|
||||||
|
|
||||||
|
HWND midKey = table[mid].key;
|
||||||
|
int cmp = midKey - key;
|
||||||
|
if( cmp < 0 )
|
||||||
|
low = mid + 1;
|
||||||
|
else if( cmp > 0 )
|
||||||
|
high = mid - 1;
|
||||||
|
else
|
||||||
|
return mid;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -(low + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HWNDMap::ensureCapacity( int minCapacity ) {
|
||||||
|
if( minCapacity <= capacity )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// allocate new table
|
||||||
|
int newCapacity = minCapacity + INCREASE_CAPACITY;
|
||||||
|
Entry* newTable = new Entry[newCapacity];
|
||||||
|
|
||||||
|
// copy old table to new table
|
||||||
|
for( int i = 0; i < capacity; i++ )
|
||||||
|
newTable[i] = table[i];
|
||||||
|
|
||||||
|
// delete old table
|
||||||
|
delete table;
|
||||||
|
|
||||||
|
table = newTable;
|
||||||
|
capacity = newCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
void HWNDMap::dump( char* msg ) {
|
||||||
|
printf( "---- %s -----------------------\n", msg );
|
||||||
|
printf( "size %d\n", size );
|
||||||
|
printf( "capacity %d\n", capacity );
|
||||||
|
printf( "table %p\n", table );
|
||||||
|
|
||||||
|
for( int i = 0; i < capacity; i++ )
|
||||||
|
printf( " %d: %p - %p %s\n", i, table[i].key, table[i].value, i >= size ? "UNUSED" : "" );
|
||||||
|
}
|
||||||
|
*/
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* 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 <windows.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple map that uses a sorted array to store key/value pairs.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct Entry
|
||||||
|
{
|
||||||
|
HWND key;
|
||||||
|
LPVOID value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HWNDMap
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
int size; // used entries in table
|
||||||
|
int capacity; // total size of table
|
||||||
|
Entry* table;
|
||||||
|
|
||||||
|
// used to synchronize to make it thread safe
|
||||||
|
CRITICAL_SECTION criticalSection;
|
||||||
|
|
||||||
|
public:
|
||||||
|
HWNDMap();
|
||||||
|
|
||||||
|
LPVOID get( HWND key );
|
||||||
|
void put( HWND key, LPVOID value );
|
||||||
|
void remove( HWND key );
|
||||||
|
|
||||||
|
private:
|
||||||
|
int binarySearch( HWND key );
|
||||||
|
void ensureCapacity( int newCapacity );
|
||||||
|
|
||||||
|
// void dump( char* msg );
|
||||||
|
};
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* 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 <windows.h>
|
||||||
|
#include <winreg.h>
|
||||||
|
#include <winerror.h>
|
||||||
|
#include <jni.h>
|
||||||
|
#include "com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---- JNI methods ------------------------------------------------------------
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_registryGetIntValue
|
||||||
|
( JNIEnv* env, jclass cls, jstring key, jstring valueName, jint defaultValue )
|
||||||
|
{
|
||||||
|
const char* skey = env->GetStringUTFChars( key, NULL );
|
||||||
|
const char* svalueName = env->GetStringUTFChars( valueName, NULL );
|
||||||
|
|
||||||
|
DWORD data = 0;
|
||||||
|
DWORD cbData = sizeof( data );
|
||||||
|
int rc = ::RegGetValueA( HKEY_CURRENT_USER, skey, svalueName, RRF_RT_DWORD, NULL, &data, &cbData );
|
||||||
|
|
||||||
|
env->ReleaseStringUTFChars( key, skey );
|
||||||
|
env->ReleaseStringUTFChars( valueName, svalueName );
|
||||||
|
|
||||||
|
return (rc == ERROR_SUCCESS) ? data : defaultValue;
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// avoid inlining of printf()
|
||||||
|
#define _NO_CRT_STDIO_INLINE
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methods that replace C-runtime methods and allow linking/running without C-runtime.
|
||||||
|
*
|
||||||
|
* WARNING: Constructors/destructors of static objects are not invoked!
|
||||||
|
*
|
||||||
|
* https://documentation.help/Far-Manager/msdnmag-issues-01-01-hood-default.aspx.html
|
||||||
|
* www.catch22.net/tuts/win32/reducing-executable-size#the-c-runtime-and-default-libraries
|
||||||
|
* https://www.mvps.org/user32/nocrt.html
|
||||||
|
*
|
||||||
|
* see also LIBCTINY on "Downloads" page here: http://www.wheaty.net/
|
||||||
|
* or https://github.com/leepa/libctiny
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
BOOL WINAPI _DllMainCRTStartup( HINSTANCE instance, DWORD reason, LPVOID reserved ) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* __cdecl operator new( size_t cb ) {
|
||||||
|
return ::HeapAlloc( ::GetProcessHeap(), HEAP_ZERO_MEMORY, cb );
|
||||||
|
}
|
||||||
|
|
||||||
|
void* __cdecl operator new[]( size_t cb ) {
|
||||||
|
return ::HeapAlloc( ::GetProcessHeap(), HEAP_ZERO_MEMORY, cb );
|
||||||
|
}
|
||||||
|
|
||||||
|
void __cdecl operator delete( void* pv, size_t cb ) {
|
||||||
|
if( pv != NULL )
|
||||||
|
::HeapFree( ::GetProcessHeap(), 0, pv );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
extern "C"
|
||||||
|
int __cdecl printf( const char* format, ... ) {
|
||||||
|
char szBuff[1024];
|
||||||
|
int retValue;
|
||||||
|
DWORD cbWritten;
|
||||||
|
va_list argptr;
|
||||||
|
|
||||||
|
va_start( argptr, format );
|
||||||
|
retValue = wvsprintfA( szBuff, format, argptr );
|
||||||
|
va_end( argptr );
|
||||||
|
|
||||||
|
WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), szBuff, retValue, &cbWritten, NULL );
|
||||||
|
|
||||||
|
return retValue;
|
||||||
|
}
|
||||||
|
*/
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||||
|
#include <jni.h>
|
||||||
|
/* Header for class com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder */
|
||||||
|
|
||||||
|
#ifndef _Included_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder
|
||||||
|
#define _Included_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
* Class: com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder
|
||||||
|
* Method: registryGetIntValue
|
||||||
|
* Signature: (Ljava/lang/String;Ljava/lang/String;I)I
|
||||||
|
*/
|
||||||
|
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_registryGetIntValue
|
||||||
|
(JNIEnv *, jclass, jstring, jstring, jint);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||||
|
#include <jni.h>
|
||||||
|
/* Header for class com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc */
|
||||||
|
|
||||||
|
#ifndef _Included_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc
|
||||||
|
#define _Included_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
#undef com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTCLIENT
|
||||||
|
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTCLIENT 1L
|
||||||
|
#undef com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTCAPTION
|
||||||
|
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTCAPTION 2L
|
||||||
|
#undef com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTSYSMENU
|
||||||
|
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTSYSMENU 3L
|
||||||
|
#undef com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTTOP
|
||||||
|
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTTOP 12L
|
||||||
|
/*
|
||||||
|
* Class: com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc
|
||||||
|
* Method: installImpl
|
||||||
|
* Signature: (Ljava/awt/Window;)J
|
||||||
|
*/
|
||||||
|
JNIEXPORT jlong JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_00024WndProc_installImpl
|
||||||
|
(JNIEnv *, jobject, jobject);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc
|
||||||
|
* Method: uninstallImpl
|
||||||
|
* Signature: (J)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_00024WndProc_uninstallImpl
|
||||||
|
(JNIEnv *, jobject, jlong);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
@@ -27,12 +27,12 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation( project( ":flatlaf-core" ) )
|
implementation( project( ":flatlaf-core" ) )
|
||||||
implementation( project( ":flatlaf-natives-jna" ) )
|
|
||||||
implementation( project( ":flatlaf-extras" ) )
|
implementation( project( ":flatlaf-extras" ) )
|
||||||
implementation( project( ":flatlaf-swingx" ) )
|
implementation( project( ":flatlaf-swingx" ) )
|
||||||
implementation( project( ":flatlaf-jide-oss" ) )
|
implementation( project( ":flatlaf-jide-oss" ) )
|
||||||
implementation( project( ":flatlaf-intellij-themes" ) )
|
implementation( project( ":flatlaf-intellij-themes" ) )
|
||||||
implementation( project( ":flatlaf-demo" ) )
|
implementation( project( ":flatlaf-demo" ) )
|
||||||
|
// implementation( project( ":flatlaf-natives-jna" ) )
|
||||||
|
|
||||||
implementation( "com.miglayout:miglayout-swing:5.3-SNAPSHOT" )
|
implementation( "com.miglayout:miglayout-swing:5.3-SNAPSHOT" )
|
||||||
implementation( "com.jgoodies:jgoodies-forms:1.9.0" )
|
implementation( "com.jgoodies:jgoodies-forms:1.9.0" )
|
||||||
|
|||||||
@@ -1143,6 +1143,7 @@ TitlePane.menuBarEmbedded true
|
|||||||
TitlePane.menuBarMargins 0,8,0,22 javax.swing.plaf.InsetsUIResource [UI]
|
TitlePane.menuBarMargins 0,8,0,22 javax.swing.plaf.InsetsUIResource [UI]
|
||||||
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
|
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
|
||||||
TitlePane.titleMargins 3,8,3,8 javax.swing.plaf.InsetsUIResource [UI]
|
TitlePane.titleMargins 3,8,3,8 javax.swing.plaf.InsetsUIResource [UI]
|
||||||
|
TitlePane.useWindowDecorations true
|
||||||
|
|
||||||
|
|
||||||
#---- TitledBorder ----
|
#---- TitledBorder ----
|
||||||
|
|||||||
@@ -1148,6 +1148,7 @@ TitlePane.menuBarEmbedded true
|
|||||||
TitlePane.menuBarMargins 0,8,0,22 javax.swing.plaf.InsetsUIResource [UI]
|
TitlePane.menuBarMargins 0,8,0,22 javax.swing.plaf.InsetsUIResource [UI]
|
||||||
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
|
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
|
||||||
TitlePane.titleMargins 3,8,3,8 javax.swing.plaf.InsetsUIResource [UI]
|
TitlePane.titleMargins 3,8,3,8 javax.swing.plaf.InsetsUIResource [UI]
|
||||||
|
TitlePane.useWindowDecorations true
|
||||||
|
|
||||||
|
|
||||||
#---- TitledBorder ----
|
#---- TitledBorder ----
|
||||||
|
|||||||
@@ -1140,6 +1140,7 @@ TitlePane.menuBarEmbedded true
|
|||||||
TitlePane.menuBarMargins 0,8,0,22 javax.swing.plaf.InsetsUIResource [UI]
|
TitlePane.menuBarMargins 0,8,0,22 javax.swing.plaf.InsetsUIResource [UI]
|
||||||
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
|
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
|
||||||
TitlePane.titleMargins 3,8,3,8 javax.swing.plaf.InsetsUIResource [UI]
|
TitlePane.titleMargins 3,8,3,8 javax.swing.plaf.InsetsUIResource [UI]
|
||||||
|
TitlePane.useWindowDecorations true
|
||||||
|
|
||||||
|
|
||||||
#---- TitledBorder ----
|
#---- TitledBorder ----
|
||||||
|
|||||||
@@ -50,9 +50,6 @@ public class FlatNativeWindowBorderTest
|
|||||||
FlatLightLaf.install();
|
FlatLightLaf.install();
|
||||||
FlatInspector.install( "ctrl shift alt X" );
|
FlatInspector.install( "ctrl shift alt X" );
|
||||||
|
|
||||||
JFrame.setDefaultLookAndFeelDecorated( true );
|
|
||||||
JDialog.setDefaultLookAndFeelDecorated( true );
|
|
||||||
|
|
||||||
mainFrame = showFrame();
|
mainFrame = showFrame();
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -853,6 +853,7 @@ TitlePane.menuBarEmbedded
|
|||||||
TitlePane.menuBarMargins
|
TitlePane.menuBarMargins
|
||||||
TitlePane.restoreIcon
|
TitlePane.restoreIcon
|
||||||
TitlePane.titleMargins
|
TitlePane.titleMargins
|
||||||
|
TitlePane.useWindowDecorations
|
||||||
TitledBorder.border
|
TitledBorder.border
|
||||||
TitledBorder.font
|
TitledBorder.font
|
||||||
TitledBorder.titleColor
|
TitledBorder.titleColor
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ include( "flatlaf-demo" )
|
|||||||
include( "flatlaf-testing" )
|
include( "flatlaf-testing" )
|
||||||
include( "flatlaf-theme-editor" )
|
include( "flatlaf-theme-editor" )
|
||||||
|
|
||||||
|
includeProject( "flatlaf-natives-windows", "flatlaf-natives/flatlaf-natives-windows" )
|
||||||
includeProject( "flatlaf-natives-jna", "flatlaf-natives/flatlaf-natives-jna" )
|
includeProject( "flatlaf-natives-jna", "flatlaf-natives/flatlaf-natives-jna" )
|
||||||
|
|
||||||
fun includeProject( projectPath: String, projectDir: String ) {
|
fun includeProject( projectPath: String, projectDir: String ) {
|
||||||
|
|||||||
Reference in New Issue
Block a user