mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-11 06:27:13 -06:00
Merge PR #579: Linux window decorations: native move window and system menu
This commit is contained in:
@@ -55,6 +55,24 @@ class FlatNativeLibrary
|
||||
libraryName += "_64";
|
||||
|
||||
// load jawt native library
|
||||
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 do not have this problem.
|
||||
loadJAWT();
|
||||
}
|
||||
} else if( SystemInfo.isLinux && SystemInfo.isX86_64 ) {
|
||||
// Linux: requires x86_64
|
||||
|
||||
libraryName = "flatlaf-linux-x86_64";
|
||||
|
||||
// Load jawt.so (part of JRE) explicitly because it is not found
|
||||
// in all Java versions/distributions.
|
||||
// E.g. not found in Java 13 and later from openjdk.java.net.
|
||||
// There seems to be also differences between distributions.
|
||||
// E.g. Adoptium Java 17 does not need this, but Java 17 from openjdk.java.net does.
|
||||
loadJAWT();
|
||||
} else
|
||||
return; // no native library available for current OS or CPU architecture
|
||||
@@ -77,14 +95,6 @@ class FlatNativeLibrary
|
||||
}
|
||||
|
||||
private static void loadJAWT() {
|
||||
if( SystemInfo.isJava_9_orLater )
|
||||
return;
|
||||
|
||||
// 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 do not have this problem.
|
||||
try {
|
||||
System.loadLibrary( "jawt" );
|
||||
} catch( UnsatisfiedLinkError ex ) {
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2022 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.Point;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
|
||||
/**
|
||||
* Native methods for Linux.
|
||||
* <p>
|
||||
* <b>Note</b>: This is private API. Do not use!
|
||||
*
|
||||
* @author Karl Tauber
|
||||
* @since 2.5
|
||||
*/
|
||||
class FlatNativeLinuxLibrary
|
||||
{
|
||||
static boolean isLoaded() {
|
||||
return FlatNativeLibrary.isLoaded();
|
||||
}
|
||||
|
||||
// direction for _NET_WM_MOVERESIZE message
|
||||
// see https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html
|
||||
static final int MOVE = 8;
|
||||
|
||||
private static Boolean isXWindowSystem;
|
||||
|
||||
private static boolean isXWindowSystem() {
|
||||
if( isXWindowSystem == null )
|
||||
isXWindowSystem = Toolkit.getDefaultToolkit().getClass().getName().endsWith( ".XToolkit" );
|
||||
return isXWindowSystem;
|
||||
}
|
||||
|
||||
static boolean isWMUtilsSupported( Window window ) {
|
||||
return hasCustomDecoration( window ) && isXWindowSystem() && isLoaded();
|
||||
}
|
||||
|
||||
static boolean moveOrResizeWindow( Window window, MouseEvent e, int direction ) {
|
||||
Point pt = scale( window, e.getLocationOnScreen() );
|
||||
return xMoveOrResizeWindow( window, pt.x, pt.y, direction );
|
||||
|
||||
/*
|
||||
try {
|
||||
Class<?> cls = Class.forName( "com.formdev.flatlaf.natives.jna.linux.X11WmUtils" );
|
||||
java.lang.reflect.Method m = cls.getMethod( "xMoveOrResizeWindow", Window.class, int.class, int.class, int.class );
|
||||
return (Boolean) m.invoke( null, window, pt.x, pt.y, direction );
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
static boolean showWindowMenu( Window window, MouseEvent e ) {
|
||||
Point pt = scale( window, e.getLocationOnScreen() );
|
||||
return xShowWindowMenu( window, pt.x, pt.y );
|
||||
|
||||
/*
|
||||
try {
|
||||
Class<?> cls = Class.forName( "com.formdev.flatlaf.natives.jna.linux.X11WmUtils" );
|
||||
java.lang.reflect.Method m = cls.getMethod( "xShowWindowMenu", Window.class, int.class, int.class );
|
||||
return (Boolean) m.invoke( null, window, pt.x, pt.y );
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
private static Point scale( Window window, Point pt ) {
|
||||
AffineTransform transform = window.getGraphicsConfiguration().getDefaultTransform();
|
||||
int x = (int) Math.round( pt.x * transform.getScaleX() );
|
||||
int y = (int) Math.round( pt.y * transform.getScaleY() );
|
||||
return new Point( x, y );
|
||||
}
|
||||
|
||||
// X Window System
|
||||
private static native boolean xMoveOrResizeWindow( Window window, int x, int y, int direction );
|
||||
private static native boolean xShowWindowMenu( Window window, int x, int y );
|
||||
|
||||
private static boolean hasCustomDecoration( Window window ) {
|
||||
return (window instanceof JFrame && JFrame.isDefaultLookAndFeelDecorated() && ((JFrame)window).isUndecorated()) ||
|
||||
(window instanceof JDialog && JDialog.isDefaultLookAndFeelDecorated() && ((JDialog)window).isUndecorated());
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import java.awt.Image;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.ComponentEvent;
|
||||
@@ -357,6 +358,7 @@ public class FlatTitlePane
|
||||
restoreButton.setVisible( resizable && maximized );
|
||||
|
||||
if( maximized &&
|
||||
!(SystemInfo.isLinux && FlatNativeLinuxLibrary.isWMUtilsSupported( window )) &&
|
||||
rootPane.getClientProperty( "_flatlaf.maximizedBoundsUpToDate" ) == null )
|
||||
{
|
||||
rootPane.putClientProperty( "_flatlaf.maximizedBoundsUpToDate", null );
|
||||
@@ -737,6 +739,17 @@ debug*/
|
||||
}
|
||||
}
|
||||
|
||||
private void maximizeOrRestore() {
|
||||
if( !(window instanceof Frame) || !((Frame)window).isResizable() )
|
||||
return;
|
||||
|
||||
Frame frame = (Frame) window;
|
||||
if( (frame.getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 )
|
||||
restore();
|
||||
else
|
||||
maximize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the window.
|
||||
*/
|
||||
@@ -1154,23 +1167,30 @@ debug*/
|
||||
//---- interface MouseListener ----
|
||||
|
||||
private Point dragOffset;
|
||||
private boolean nativeMove;
|
||||
private long lastSingleClickWhen;
|
||||
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e ) {
|
||||
// on Linux, when using native library, the mouse clicked event
|
||||
// is usually not sent and maximize/restore is done in mouse pressed event
|
||||
// this check is here for the case that a mouse clicked event comes thru for some reason
|
||||
if( SystemInfo.isLinux && FlatNativeLinuxLibrary.isWMUtilsSupported( window ) ) {
|
||||
// see comment in mousePressed()
|
||||
if( lastSingleClickWhen != 0 && (e.getWhen() - lastSingleClickWhen) <= getMultiClickInterval() ) {
|
||||
lastSingleClickWhen = 0;
|
||||
maximizeOrRestore();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if( e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton( e ) ) {
|
||||
if( e.getSource() == iconLabel ) {
|
||||
// double-click on icon closes window
|
||||
close();
|
||||
} else if( !hasNativeCustomDecoration() &&
|
||||
window instanceof Frame &&
|
||||
((Frame)window).isResizable() )
|
||||
{
|
||||
} else if( !hasNativeCustomDecoration() ) {
|
||||
// maximize/restore on double-click
|
||||
Frame frame = (Frame) window;
|
||||
if( (frame.getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 )
|
||||
restore();
|
||||
else
|
||||
maximize();
|
||||
maximizeOrRestore();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1180,10 +1200,56 @@ debug*/
|
||||
if( window == null )
|
||||
return; // should newer occur
|
||||
|
||||
// on Linux, show window menu
|
||||
if( SwingUtilities.isRightMouseButton( e ) &&
|
||||
SystemInfo.isLinux && FlatNativeLinuxLibrary.isWMUtilsSupported( window ) )
|
||||
{
|
||||
e.consume();
|
||||
FlatNativeLinuxLibrary.showWindowMenu( window, e );
|
||||
return;
|
||||
}
|
||||
|
||||
if( !SwingUtilities.isLeftMouseButton( e ) )
|
||||
return;
|
||||
|
||||
dragOffset = SwingUtilities.convertPoint( FlatTitlePane.this, e.getPoint(), window );
|
||||
nativeMove = false;
|
||||
|
||||
// on Linux, move or maximize/restore window
|
||||
if( SystemInfo.isLinux && FlatNativeLinuxLibrary.isWMUtilsSupported( window ) ) {
|
||||
// The fired Java mouse events, when doing a double-click and the first click
|
||||
// sends a _NET_WM_MOVERESIZE message, are different for various Linux distributions:
|
||||
// CentOS 7 (GNOME 3.28.2, X11): PRESSED(clickCount=1) PRESSED(clickCount=2) RELEASED(clickCount=2)
|
||||
// Ubuntu 20.04 (GNOME 3.36.1, X11): PRESSED(clickCount=1) PRESSED(clickCount=2) RELEASED(clickCount=2)
|
||||
// Ubuntu 22.04 (GNOME 42.2, Wayland): PRESSED(clickCount=1) RELEASED(clickCount=1) CLICKED(clickCount=1)
|
||||
// Kubuntu 22.04 (KDE 5.24.4, X11): PRESSED(clickCount=1) PRESSED(clickCount=1) RELEASED(clickCount=1)
|
||||
|
||||
// double-click is not always recognized in Java when using _NET_WM_MOVERESIZE message
|
||||
int clickCount = e.getClickCount();
|
||||
if( clickCount == 1 && lastSingleClickWhen != 0 && (e.getWhen() - lastSingleClickWhen) <= getMultiClickInterval() )
|
||||
clickCount = 2;
|
||||
|
||||
switch( clickCount ) {
|
||||
case 1:
|
||||
// move window via _NET_WM_MOVERESIZE message
|
||||
e.consume();
|
||||
nativeMove = FlatNativeLinuxLibrary.moveOrResizeWindow( window, e, FlatNativeLinuxLibrary.MOVE );
|
||||
lastSingleClickWhen = e.getWhen();
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// maximize/restore on double-click
|
||||
// also done here because no mouse clicked event is sent when using _NET_WM_MOVERESIZE message
|
||||
lastSingleClickWhen = 0;
|
||||
maximizeOrRestore();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getMultiClickInterval() {
|
||||
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "awt.multiClickInterval" );
|
||||
return (value instanceof Integer) ? (Integer) value : 500;
|
||||
}
|
||||
|
||||
@Override public void mouseReleased( MouseEvent e ) {}
|
||||
@@ -1194,9 +1260,12 @@ debug*/
|
||||
|
||||
@Override
|
||||
public void mouseDragged( MouseEvent e ) {
|
||||
if( window == null )
|
||||
if( window == null || dragOffset == null )
|
||||
return; // should newer occur
|
||||
|
||||
if( nativeMove )
|
||||
return;
|
||||
|
||||
if( !SwingUtilities.isLeftMouseButton( e ) )
|
||||
return;
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ public class NativeLibrary
|
||||
System.load( libraryFile.getAbsolutePath() );
|
||||
return true;
|
||||
} catch( Throwable ex ) {
|
||||
log( null, ex );
|
||||
log( ex.getMessage(), ex );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user