mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-07 06:20:53 +03:00
Native window decorations: show Windows 11 snap layouts menu when hovering the mouse over the maximize button (issues #397 and #407)
This commit is contained in:
@@ -12,6 +12,8 @@ FlatLaf Change Log
|
|||||||
- Style classes allow defining style rules at a single place (in UI defaults)
|
- Style classes allow defining style rules at a single place (in UI defaults)
|
||||||
and use them in any component. (PR #388)\
|
and use them in any component. (PR #388)\
|
||||||
E.g.: `mySlider.putClientProperty( "FlatLaf.styleClass", "myclass" );`
|
E.g.: `mySlider.putClientProperty( "FlatLaf.styleClass", "myclass" );`
|
||||||
|
- Native window decorations: Show Windows 11 snap layouts menu when hovering the
|
||||||
|
mouse over the maximize button. (issues #397 and #407)
|
||||||
- TextField, FormattedTextField and PasswordField: Support leading and trailing
|
- TextField, FormattedTextField and PasswordField: Support leading and trailing
|
||||||
icons (set client property `JTextField.leadingIcon` or
|
icons (set client property `JTextField.leadingIcon` or
|
||||||
`JTextField.trailingIcon` to an `Icon`). (PR #378; issue #368)
|
`JTextField.trailingIcon` to an `Icon`). (PR #378; issue #368)
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ public class FlatNativeWindowBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight,
|
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight,
|
||||||
List<Rectangle> hitTestSpots, Rectangle appIconBounds )
|
List<Rectangle> hitTestSpots, Rectangle appIconBounds, Rectangle maximizeButtonBounds )
|
||||||
{
|
{
|
||||||
if( canUseJBRCustomDecorations ) {
|
if( canUseJBRCustomDecorations ) {
|
||||||
JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots );
|
JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots );
|
||||||
@@ -245,9 +245,7 @@ public class FlatNativeWindowBorder
|
|||||||
if( !isSupported() )
|
if( !isSupported() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
nativeProvider.setTitleBarHeight( window, titleBarHeight );
|
nativeProvider.updateTitleBarInfo( window, titleBarHeight, hitTestSpots, appIconBounds, maximizeButtonBounds );
|
||||||
nativeProvider.setTitleBarHitTestSpots( window, hitTestSpots );
|
|
||||||
nativeProvider.setTitleBarAppIconBounds( window, appIconBounds );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean showWindow( Window window, int cmd ) {
|
static boolean showWindow( Window window, int cmd ) {
|
||||||
@@ -292,9 +290,8 @@ public class FlatNativeWindowBorder
|
|||||||
{
|
{
|
||||||
boolean hasCustomDecoration( Window window );
|
boolean hasCustomDecoration( Window window );
|
||||||
void setHasCustomDecoration( Window window, boolean hasCustomDecoration );
|
void setHasCustomDecoration( Window window, boolean hasCustomDecoration );
|
||||||
void setTitleBarHeight( Window window, int titleBarHeight );
|
void updateTitleBarInfo( Window window, int titleBarHeight, List<Rectangle> hitTestSpots,
|
||||||
void setTitleBarHitTestSpots( Window window, List<Rectangle> hitTestSpots );
|
Rectangle appIconBounds, Rectangle maximizeButtonBounds );
|
||||||
void setTitleBarAppIconBounds( Window window, Rectangle appIconBounds );
|
|
||||||
|
|
||||||
// commands for showWindow(); values must match Win32 API
|
// commands for showWindow(); values must match Win32 API
|
||||||
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
|
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
|
||||||
|
|||||||
@@ -527,11 +527,17 @@ public class FlatTitlePane
|
|||||||
g.drawRect( r.x - offset.x, r.y - offset.y, r.width - 1, r.height - 1 );
|
g.drawRect( r.x - offset.x, r.y - offset.y, r.width - 1, r.height - 1 );
|
||||||
}
|
}
|
||||||
if( debugAppIconBounds != null ) {
|
if( debugAppIconBounds != null ) {
|
||||||
g.setColor( Color.blue);
|
g.setColor( Color.blue );
|
||||||
Point offset = SwingUtilities.convertPoint( this, 0, 0, window );
|
Point offset = SwingUtilities.convertPoint( this, 0, 0, window );
|
||||||
Rectangle r = debugAppIconBounds;
|
Rectangle r = debugAppIconBounds;
|
||||||
g.drawRect( r.x - offset.x, r.y - offset.y, r.width - 1, r.height - 1 );
|
g.drawRect( r.x - offset.x, r.y - offset.y, r.width - 1, r.height - 1 );
|
||||||
}
|
}
|
||||||
|
if( debugMaximizeButtonBounds != null ) {
|
||||||
|
g.setColor( Color.blue );
|
||||||
|
Point offset = SwingUtilities.convertPoint( this, 0, 0, window );
|
||||||
|
Rectangle r = debugMaximizeButtonBounds;
|
||||||
|
g.drawRect( r.x - offset.x, r.y - offset.y, r.width - 1, r.height - 1 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
debug*/
|
debug*/
|
||||||
|
|
||||||
@@ -722,6 +728,7 @@ debug*/
|
|||||||
|
|
||||||
List<Rectangle> hitTestSpots = new ArrayList<>();
|
List<Rectangle> hitTestSpots = new ArrayList<>();
|
||||||
Rectangle appIconBounds = null;
|
Rectangle appIconBounds = null;
|
||||||
|
|
||||||
if( iconLabel.isVisible() ) {
|
if( iconLabel.isVisible() ) {
|
||||||
// compute real icon size (without insets; 1px wider for easier hitting)
|
// compute real icon size (without insets; 1px wider for easier hitting)
|
||||||
Point location = SwingUtilities.convertPoint( iconLabel, 0, 0, window );
|
Point location = SwingUtilities.convertPoint( iconLabel, 0, 0, window );
|
||||||
@@ -753,6 +760,13 @@ debug*/
|
|||||||
appIconBounds = iconBounds;
|
appIconBounds = iconBounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JButton maxButton = maximizeButton.isVisible()
|
||||||
|
? maximizeButton
|
||||||
|
: (restoreButton.isVisible() ? restoreButton : null);
|
||||||
|
Rectangle maximizeButtonBounds = (maxButton != null)
|
||||||
|
? SwingUtilities.convertRectangle( maxButton.getParent(), maxButton.getBounds(), window )
|
||||||
|
: null;
|
||||||
|
|
||||||
Rectangle r = getNativeHitTestSpot( buttonPanel );
|
Rectangle r = getNativeHitTestSpot( buttonPanel );
|
||||||
if( r != null )
|
if( r != null )
|
||||||
hitTestSpots.add( r );
|
hitTestSpots.add( r );
|
||||||
@@ -787,12 +801,14 @@ debug*/
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatNativeWindowBorder.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots, appIconBounds );
|
FlatNativeWindowBorder.setTitleBarHeightAndHitTestSpots( window, titleBarHeight,
|
||||||
|
hitTestSpots, appIconBounds, maximizeButtonBounds );
|
||||||
|
|
||||||
/*debug
|
/*debug
|
||||||
debugTitleBarHeight = titleBarHeight;
|
debugTitleBarHeight = titleBarHeight;
|
||||||
debugHitTestSpots = hitTestSpots;
|
debugHitTestSpots = hitTestSpots;
|
||||||
debugAppIconBounds = appIconBounds;
|
debugAppIconBounds = appIconBounds;
|
||||||
|
debugMaximizeButtonBounds = maximizeButtonBounds;
|
||||||
repaint();
|
repaint();
|
||||||
debug*/
|
debug*/
|
||||||
}
|
}
|
||||||
@@ -815,6 +831,7 @@ debug*/
|
|||||||
private int debugTitleBarHeight;
|
private int debugTitleBarHeight;
|
||||||
private List<Rectangle> debugHitTestSpots;
|
private List<Rectangle> debugHitTestSpots;
|
||||||
private Rectangle debugAppIconBounds;
|
private Rectangle debugAppIconBounds;
|
||||||
|
private Rectangle debugMaximizeButtonBounds;
|
||||||
debug*/
|
debug*/
|
||||||
|
|
||||||
//---- class FlatTitlePaneBorder ------------------------------------------
|
//---- class FlatTitlePaneBorder ------------------------------------------
|
||||||
|
|||||||
@@ -54,6 +54,10 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
// https://github.com/oberth/custom-chrome
|
// https://github.com/oberth/custom-chrome
|
||||||
// https://github.com/rossy/borderless-window
|
// https://github.com/rossy/borderless-window
|
||||||
//
|
//
|
||||||
|
// Windows 11
|
||||||
|
// https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/apply-snap-layout-menu
|
||||||
|
// https://github.com/dotnet/wpf/issues/4825#issuecomment-930442736
|
||||||
|
//
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Native window border support for Windows 10 when using custom decorations.
|
* Native window border support for Windows 10 when using custom decorations.
|
||||||
@@ -177,30 +181,17 @@ class FlatWindowsNativeWindowBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTitleBarHeight( Window window, int titleBarHeight ) {
|
public void updateTitleBarInfo( Window window, int titleBarHeight, List<Rectangle> hitTestSpots,
|
||||||
|
Rectangle appIconBounds, Rectangle maximizeButtonBounds )
|
||||||
|
{
|
||||||
WndProc wndProc = windowsMap.get( window );
|
WndProc wndProc = windowsMap.get( window );
|
||||||
if( wndProc == null )
|
if( wndProc == null )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wndProc.titleBarHeight = titleBarHeight;
|
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()] );
|
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;
|
wndProc.appIconBounds = (appIconBounds != null) ? new Rectangle( appIconBounds ) : null;
|
||||||
|
wndProc.maximizeButtonBounds = (maximizeButtonBounds != null) ? new Rectangle( maximizeButtonBounds ) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -303,14 +294,17 @@ class FlatWindowsNativeWindowBorder
|
|||||||
HTCLIENT = 1,
|
HTCLIENT = 1,
|
||||||
HTCAPTION = 2,
|
HTCAPTION = 2,
|
||||||
HTSYSMENU = 3,
|
HTSYSMENU = 3,
|
||||||
|
HTMAXBUTTON = 9,
|
||||||
HTTOP = 12;
|
HTTOP = 12;
|
||||||
|
|
||||||
private Window window;
|
private Window window;
|
||||||
private final long hwnd;
|
private final long hwnd;
|
||||||
|
|
||||||
|
// Swing coordinates/values may be scaled on a HiDPI screen
|
||||||
private int titleBarHeight;
|
private int titleBarHeight;
|
||||||
private Rectangle[] hitTestSpots;
|
private Rectangle[] hitTestSpots;
|
||||||
private Rectangle appIconBounds;
|
private Rectangle appIconBounds;
|
||||||
|
private Rectangle maximizeButtonBounds;
|
||||||
|
|
||||||
WndProc( Window window ) {
|
WndProc( Window window ) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
@@ -355,7 +349,7 @@ class FlatWindowsNativeWindowBorder
|
|||||||
|
|
||||||
// invoked from native code
|
// invoked from native code
|
||||||
private int onNcHitTest( int x, int y, boolean isOnResizeBorder ) {
|
private int onNcHitTest( int x, int y, boolean isOnResizeBorder ) {
|
||||||
// scale-down mouse x/y
|
// scale-down mouse x/y because Swing coordinates/values may be scaled on a HiDPI screen
|
||||||
Point pt = scaleDown( x, y );
|
Point pt = scaleDown( x, y );
|
||||||
int sx = pt.x;
|
int sx = pt.x;
|
||||||
int sy = pt.y;
|
int sy = pt.y;
|
||||||
@@ -366,6 +360,12 @@ class FlatWindowsNativeWindowBorder
|
|||||||
if( appIconBounds != null && appIconBounds.contains( sx, sy ) )
|
if( appIconBounds != null && appIconBounds.contains( sx, sy ) )
|
||||||
return HTSYSMENU;
|
return HTSYSMENU;
|
||||||
|
|
||||||
|
// return HTMAXBUTTON if mouse is over maximize/restore button
|
||||||
|
// - hovering mouse over HTMAXBUTTON area shows snap layouts menu on Windows 11
|
||||||
|
// https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/apply-snap-layout-menu
|
||||||
|
if( maximizeButtonBounds != null && maximizeButtonBounds.contains( sx, sy ) )
|
||||||
|
return HTMAXBUTTON;
|
||||||
|
|
||||||
boolean isOnTitleBar = (sy < titleBarHeight);
|
boolean isOnTitleBar = (sy < titleBarHeight);
|
||||||
|
|
||||||
if( isOnTitleBar ) {
|
if( isOnTitleBar ) {
|
||||||
|
|||||||
@@ -20,6 +20,6 @@ plugins {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation( project( ":flatlaf-core" ) )
|
implementation( project( ":flatlaf-core" ) )
|
||||||
implementation( "net.java.dev.jna:jna:5.7.0" )
|
implementation( "net.java.dev.jna:jna:5.10.0" )
|
||||||
implementation( "net.java.dev.jna:jna-platform:5.7.0" )
|
implementation( "net.java.dev.jna:jna-platform:5.10.0" )
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ import com.sun.jna.Pointer;
|
|||||||
import com.sun.jna.Structure;
|
import com.sun.jna.Structure;
|
||||||
import com.sun.jna.Structure.FieldOrder;
|
import com.sun.jna.Structure.FieldOrder;
|
||||||
import com.sun.jna.platform.win32.Advapi32Util;
|
import com.sun.jna.platform.win32.Advapi32Util;
|
||||||
import com.sun.jna.platform.win32.BaseTSD;
|
import com.sun.jna.platform.win32.BaseTSD.LONG_PTR;
|
||||||
import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR;
|
import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR;
|
||||||
import com.sun.jna.platform.win32.GDI32;
|
import com.sun.jna.platform.win32.GDI32;
|
||||||
import com.sun.jna.platform.win32.Shell32;
|
import com.sun.jna.platform.win32.Shell32;
|
||||||
@@ -77,6 +77,10 @@ import com.sun.jna.win32.W32APIOptions;
|
|||||||
// https://github.com/oberth/custom-chrome
|
// https://github.com/oberth/custom-chrome
|
||||||
// https://github.com/rossy/borderless-window
|
// https://github.com/rossy/borderless-window
|
||||||
//
|
//
|
||||||
|
// Windows 11
|
||||||
|
// https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/apply-snap-layout-menu
|
||||||
|
// https://github.com/dotnet/wpf/issues/4825#issuecomment-930442736
|
||||||
|
//
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Native window border support for Windows 10 when using custom decorations.
|
* Native window border support for Windows 10 when using custom decorations.
|
||||||
@@ -160,30 +164,17 @@ public class FlatWindowsNativeWindowBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTitleBarHeight( Window window, int titleBarHeight ) {
|
public void updateTitleBarInfo( Window window, int titleBarHeight, List<Rectangle> hitTestSpots,
|
||||||
|
Rectangle appIconBounds, Rectangle maximizeButtonBounds )
|
||||||
|
{
|
||||||
WndProc wndProc = windowsMap.get( window );
|
WndProc wndProc = windowsMap.get( window );
|
||||||
if( wndProc == null )
|
if( wndProc == null )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wndProc.titleBarHeight = titleBarHeight;
|
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()] );
|
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;
|
wndProc.appIconBounds = (appIconBounds != null) ? new Rectangle( appIconBounds ) : null;
|
||||||
|
wndProc.maximizeButtonBounds = (maximizeButtonBounds != null) ? new Rectangle( maximizeButtonBounds ) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -293,7 +284,16 @@ public class FlatWindowsNativeWindowBorder
|
|||||||
WM_ERASEBKGND = 0x0014,
|
WM_ERASEBKGND = 0x0014,
|
||||||
WM_NCCALCSIZE = 0x0083,
|
WM_NCCALCSIZE = 0x0083,
|
||||||
WM_NCHITTEST = 0x0084,
|
WM_NCHITTEST = 0x0084,
|
||||||
|
|
||||||
|
WM_NCMOUSEMOVE = 0x00A0,
|
||||||
|
WM_NCLBUTTONDOWN = 0x00A1,
|
||||||
|
WM_NCLBUTTONUP = 0x00A2,
|
||||||
WM_NCRBUTTONUP = 0x00A5,
|
WM_NCRBUTTONUP = 0x00A5,
|
||||||
|
|
||||||
|
WM_MOUSEMOVE= 0x0200,
|
||||||
|
WM_LBUTTONDOWN = 0x0201,
|
||||||
|
WM_LBUTTONUP = 0x0202,
|
||||||
|
|
||||||
WM_DWMCOLORIZATIONCOLORCHANGED = 0x0320;
|
WM_DWMCOLORIZATIONCOLORCHANGED = 0x0320;
|
||||||
|
|
||||||
// WM_SIZE wParam
|
// WM_SIZE wParam
|
||||||
@@ -306,6 +306,7 @@ public class FlatWindowsNativeWindowBorder
|
|||||||
HTCLIENT = 1,
|
HTCLIENT = 1,
|
||||||
HTCAPTION = 2,
|
HTCAPTION = 2,
|
||||||
HTSYSMENU = 3,
|
HTSYSMENU = 3,
|
||||||
|
HTMAXBUTTON = 9,
|
||||||
HTTOP = 12;
|
HTTOP = 12;
|
||||||
|
|
||||||
private static final int ABS_AUTOHIDE = 0x0000001;
|
private static final int ABS_AUTOHIDE = 0x0000001;
|
||||||
@@ -328,13 +329,15 @@ public class FlatWindowsNativeWindowBorder
|
|||||||
|
|
||||||
private Window window;
|
private Window window;
|
||||||
private final HWND hwnd;
|
private final HWND hwnd;
|
||||||
private final BaseTSD.LONG_PTR defaultWndProc;
|
private final LONG_PTR defaultWndProc;
|
||||||
private int wmSizeWParam = -1;
|
private int wmSizeWParam = -1;
|
||||||
private HBRUSH background;
|
private HBRUSH background;
|
||||||
|
|
||||||
|
// Swing coordinates/values may be scaled on a HiDPI screen
|
||||||
private int titleBarHeight;
|
private int titleBarHeight;
|
||||||
private Rectangle[] hitTestSpots;
|
private Rectangle[] hitTestSpots;
|
||||||
private Rectangle appIconBounds;
|
private Rectangle appIconBounds;
|
||||||
|
private Rectangle maximizeButtonBounds;
|
||||||
|
|
||||||
WndProc( Window window ) {
|
WndProc( Window window ) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
@@ -427,6 +430,26 @@ public class FlatWindowsNativeWindowBorder
|
|||||||
case WM_NCHITTEST:
|
case WM_NCHITTEST:
|
||||||
return WmNcHitTest( hwnd, uMsg, wParam, lParam );
|
return WmNcHitTest( hwnd, uMsg, wParam, lParam );
|
||||||
|
|
||||||
|
case WM_NCMOUSEMOVE:
|
||||||
|
// if mouse is moved over some non-client areas,
|
||||||
|
// send it also to the client area to allow Swing to process it
|
||||||
|
// (required for Windows 11 maximize button)
|
||||||
|
if( wParam.longValue() == HTMAXBUTTON || wParam.longValue() == HTCAPTION || wParam.longValue() == HTSYSMENU )
|
||||||
|
sendMessageToClientArea( hwnd, WM_MOUSEMOVE, lParam );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WM_NCLBUTTONDOWN:
|
||||||
|
case WM_NCLBUTTONUP:
|
||||||
|
// if left mouse was pressed/released over maximize button,
|
||||||
|
// send it also to the client area to allow Swing to process it
|
||||||
|
// (required for Windows 11 maximize button)
|
||||||
|
if( wParam.shortValue() == HTMAXBUTTON ) {
|
||||||
|
int uClientMsg = (uMsg == WM_NCLBUTTONDOWN) ? WM_LBUTTONDOWN : WM_LBUTTONUP;
|
||||||
|
sendMessageToClientArea( hwnd, uClientMsg, lParam );
|
||||||
|
return new LRESULT( 0 );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case WM_NCRBUTTONUP:
|
case WM_NCRBUTTONUP:
|
||||||
if( wParam.longValue() == HTCAPTION || wParam.longValue() == HTSYSMENU )
|
if( wParam.longValue() == HTCAPTION || wParam.longValue() == HTSYSMENU )
|
||||||
openSystemMenu( hwnd, GET_X_LPARAM( lParam ), GET_Y_LPARAM( lParam ) );
|
openSystemMenu( hwnd, GET_X_LPARAM( lParam ), GET_Y_LPARAM( lParam ) );
|
||||||
@@ -522,7 +545,7 @@ public class FlatWindowsNativeWindowBorder
|
|||||||
|
|
||||||
boolean isMaximized = User32Ex.INSTANCE.IsZoomed( hwnd );
|
boolean isMaximized = User32Ex.INSTANCE.IsZoomed( hwnd );
|
||||||
if( isMaximized && !isFullscreen() ) {
|
if( isMaximized && !isFullscreen() ) {
|
||||||
// When a window is maximized, its size is actually a little bit more
|
// When a window is maximized, its size is actually a little bit larger
|
||||||
// than the monitor's work area. The window is positioned and sized in
|
// 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
|
// 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
|
// then the window is clipped to the monitor so that the resize handle
|
||||||
@@ -574,15 +597,12 @@ public class FlatWindowsNativeWindowBorder
|
|||||||
if( lResult.longValue() != HTCLIENT )
|
if( lResult.longValue() != HTCLIENT )
|
||||||
return lResult;
|
return lResult;
|
||||||
|
|
||||||
// get window rectangle needed to convert mouse x/y from screen to window coordinates
|
|
||||||
RECT rcWindow = new RECT();
|
|
||||||
User32.INSTANCE.GetWindowRect( hwnd, rcWindow );
|
|
||||||
|
|
||||||
// get mouse x/y in window coordinates
|
// get mouse x/y in window coordinates
|
||||||
int x = GET_X_LPARAM( lParam ) - rcWindow.left;
|
LRESULT xy = screen2windowCoordinates( hwnd, lParam );
|
||||||
int y = GET_Y_LPARAM( lParam ) - rcWindow.top;
|
int x = GET_X_LPARAM( xy );
|
||||||
|
int y = GET_Y_LPARAM( xy );
|
||||||
|
|
||||||
// scale-down mouse x/y
|
// scale-down mouse x/y because Swing coordinates/values may be scaled on a HiDPI screen
|
||||||
Point pt = scaleDown( x, y );
|
Point pt = scaleDown( x, y );
|
||||||
int sx = pt.x;
|
int sx = pt.x;
|
||||||
int sy = pt.y;
|
int sy = pt.y;
|
||||||
@@ -593,6 +613,12 @@ public class FlatWindowsNativeWindowBorder
|
|||||||
if( appIconBounds != null && appIconBounds.contains( sx, sy ) )
|
if( appIconBounds != null && appIconBounds.contains( sx, sy ) )
|
||||||
return new LRESULT( HTSYSMENU );
|
return new LRESULT( HTSYSMENU );
|
||||||
|
|
||||||
|
// return HTMAXBUTTON if mouse is over maximize/restore button
|
||||||
|
// - hovering mouse over HTMAXBUTTON area shows snap layouts menu on Windows 11
|
||||||
|
// https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/apply-snap-layout-menu
|
||||||
|
if( maximizeButtonBounds != null && maximizeButtonBounds.contains( sx, sy ) )
|
||||||
|
return new LRESULT( HTMAXBUTTON );
|
||||||
|
|
||||||
int resizeBorderHeight = getResizeHandleHeight();
|
int resizeBorderHeight = getResizeHandleHeight();
|
||||||
boolean isOnResizeBorder = (y < resizeBorderHeight) &&
|
boolean isOnResizeBorder = (y < resizeBorderHeight) &&
|
||||||
(User32.INSTANCE.GetWindowLong( hwnd, GWL_STYLE ) & WS_THICKFRAME) != 0;
|
(User32.INSTANCE.GetWindowLong( hwnd, GWL_STYLE ) & WS_THICKFRAME) != 0;
|
||||||
@@ -612,6 +638,21 @@ public class FlatWindowsNativeWindowBorder
|
|||||||
return new LRESULT( isOnResizeBorder ? HTTOP : HTCLIENT );
|
return new LRESULT( isOnResizeBorder ? HTTOP : HTCLIENT );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts screen coordinates to window coordinates.
|
||||||
|
*/
|
||||||
|
private LRESULT screen2windowCoordinates( HWND hwnd, LPARAM lParam ) {
|
||||||
|
// get window rectangle needed to convert mouse x/y from screen to window coordinates
|
||||||
|
RECT rcWindow = new RECT();
|
||||||
|
User32.INSTANCE.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;
|
||||||
|
|
||||||
|
return new LRESULT( MAKELONG( x, y ) );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the height of the little space at the top of the window used to
|
* Returns the height of the little space at the top of the window used to
|
||||||
* resize the window.
|
* resize the window.
|
||||||
@@ -678,7 +719,7 @@ public class FlatWindowsNativeWindowBorder
|
|||||||
*
|
*
|
||||||
* https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-nchittest#remarks
|
* https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-nchittest#remarks
|
||||||
*/
|
*/
|
||||||
private int GET_X_LPARAM( LPARAM lParam ) {
|
private int GET_X_LPARAM( LONG_PTR lParam ) {
|
||||||
return (short) (lParam.longValue() & 0xffff);
|
return (short) (lParam.longValue() & 0xffff);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -688,10 +729,17 @@ public class FlatWindowsNativeWindowBorder
|
|||||||
*
|
*
|
||||||
* https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-nchittest#remarks
|
* https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-nchittest#remarks
|
||||||
*/
|
*/
|
||||||
private int GET_Y_LPARAM( LPARAM lParam ) {
|
private int GET_Y_LPARAM( LONG_PTR lParam ) {
|
||||||
return (short) ((lParam.longValue() >> 16) & 0xffff);
|
return (short) ((lParam.longValue() >> 16) & 0xffff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same implementation as MAKELONG(wLow, wHigh) macro in windef.h.
|
||||||
|
*/
|
||||||
|
private long MAKELONG( int low, int high ) {
|
||||||
|
return (low & 0xffff) | ((high & 0xffff) << 16);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same implementation as RGB(r,g,b) macro in wingdi.h.
|
* Same implementation as RGB(r,g,b) macro in wingdi.h.
|
||||||
*/
|
*/
|
||||||
@@ -699,6 +747,14 @@ public class FlatWindowsNativeWindowBorder
|
|||||||
return new DWORD( (r & 0xff) | ((g & 0xff) << 8) | ((b & 0xff) << 16) );
|
return new DWORD( (r & 0xff) | ((g & 0xff) << 8) | ((b & 0xff) << 16) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendMessageToClientArea( HWND hwnd, int uMsg, LPARAM lParam ) {
|
||||||
|
// get mouse x/y in window coordinates
|
||||||
|
LRESULT xy = screen2windowCoordinates( hwnd, lParam );
|
||||||
|
|
||||||
|
// send message
|
||||||
|
User32.INSTANCE.SendMessage( hwnd, uMsg, new WPARAM(), new LPARAM( xy.longValue() ) );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the window's system menu.
|
* Opens the window's system menu.
|
||||||
* The system menu is the menu that opens when the user presses Alt+Space or
|
* The system menu is the menu that opens when the user presses Alt+Space or
|
||||||
|
|||||||
@@ -215,6 +215,26 @@ LRESULT CALLBACK FlatWndProc::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, L
|
|||||||
case WM_NCHITTEST:
|
case WM_NCHITTEST:
|
||||||
return WmNcHitTest( hwnd, uMsg, wParam, lParam );
|
return WmNcHitTest( hwnd, uMsg, wParam, lParam );
|
||||||
|
|
||||||
|
case WM_NCMOUSEMOVE:
|
||||||
|
// if mouse is moved over some non-client areas,
|
||||||
|
// send it also to the client area to allow Swing to process it
|
||||||
|
// (required for Windows 11 maximize button)
|
||||||
|
if( wParam == HTMAXBUTTON || wParam == HTCAPTION || wParam == HTSYSMENU )
|
||||||
|
sendMessageToClientArea( hwnd, WM_MOUSEMOVE, lParam );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WM_NCLBUTTONDOWN:
|
||||||
|
case WM_NCLBUTTONUP:
|
||||||
|
// if left mouse was pressed/released over maximize button,
|
||||||
|
// send it also to the client area to allow Swing to process it
|
||||||
|
// (required for Windows 11 maximize button)
|
||||||
|
if( wParam == HTMAXBUTTON ) {
|
||||||
|
int uClientMsg = (uMsg == WM_NCLBUTTONDOWN) ? WM_LBUTTONDOWN : WM_LBUTTONUP;
|
||||||
|
sendMessageToClientArea( hwnd, uClientMsg, lParam );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case WM_NCRBUTTONUP:
|
case WM_NCRBUTTONUP:
|
||||||
if( wParam == HTCAPTION || wParam == HTSYSMENU )
|
if( wParam == HTCAPTION || wParam == HTSYSMENU )
|
||||||
openSystemMenu( hwnd, GET_X_LPARAM( lParam ), GET_Y_LPARAM( lParam ) );
|
openSystemMenu( hwnd, GET_X_LPARAM( lParam ), GET_Y_LPARAM( lParam ) );
|
||||||
@@ -305,7 +325,7 @@ LRESULT FlatWndProc::WmNcCalcSize( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lP
|
|||||||
|
|
||||||
bool isMaximized = ::IsZoomed( hwnd );
|
bool isMaximized = ::IsZoomed( hwnd );
|
||||||
if( isMaximized && !isFullscreen() ) {
|
if( isMaximized && !isFullscreen() ) {
|
||||||
// When a window is maximized, its size is actually a little bit more
|
// When a window is maximized, its size is actually a little bit larger
|
||||||
// than the monitor's work area. The window is positioned and sized in
|
// 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
|
// 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
|
// then the window is clipped to the monitor so that the resize handle
|
||||||
@@ -354,6 +374,22 @@ LRESULT FlatWndProc::WmNcHitTest( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lPa
|
|||||||
if( lResult != HTCLIENT )
|
if( lResult != HTCLIENT )
|
||||||
return lResult;
|
return lResult;
|
||||||
|
|
||||||
|
// get mouse x/y in window coordinates
|
||||||
|
LRESULT xy = screen2windowCoordinates( hwnd, lParam );
|
||||||
|
int x = GET_X_LPARAM( xy );
|
||||||
|
int y = GET_Y_LPARAM( xy );
|
||||||
|
|
||||||
|
int resizeBorderHeight = getResizeHandleHeight();
|
||||||
|
bool isOnResizeBorder = (y < resizeBorderHeight) &&
|
||||||
|
(::GetWindowLong( hwnd, GWL_STYLE ) & WS_THICKFRAME) != 0;
|
||||||
|
|
||||||
|
return onNcHitTest( x, y, isOnResizeBorder );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts screen coordinates to window coordinates.
|
||||||
|
*/
|
||||||
|
LRESULT FlatWndProc::screen2windowCoordinates( HWND hwnd, LPARAM lParam ) {
|
||||||
// get window rectangle needed to convert mouse x/y from screen to window coordinates
|
// get window rectangle needed to convert mouse x/y from screen to window coordinates
|
||||||
RECT rcWindow;
|
RECT rcWindow;
|
||||||
::GetWindowRect( hwnd, &rcWindow );
|
::GetWindowRect( hwnd, &rcWindow );
|
||||||
@@ -362,11 +398,7 @@ LRESULT FlatWndProc::WmNcHitTest( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lPa
|
|||||||
int x = GET_X_LPARAM( lParam ) - rcWindow.left;
|
int x = GET_X_LPARAM( lParam ) - rcWindow.left;
|
||||||
int y = GET_Y_LPARAM( lParam ) - rcWindow.top;
|
int y = GET_Y_LPARAM( lParam ) - rcWindow.top;
|
||||||
|
|
||||||
int resizeBorderHeight = getResizeHandleHeight();
|
return MAKELONG( x, y );
|
||||||
bool isOnResizeBorder = (y < resizeBorderHeight) &&
|
|
||||||
(::GetWindowLong( hwnd, GWL_STYLE ) & WS_THICKFRAME) != 0;
|
|
||||||
|
|
||||||
return onNcHitTest( x, y, isOnResizeBorder );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -429,6 +461,14 @@ JNIEnv* FlatWndProc::getEnv() {
|
|||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FlatWndProc::sendMessageToClientArea( HWND hwnd, int uMsg, LPARAM lParam ) {
|
||||||
|
// get mouse x/y in window coordinates
|
||||||
|
LRESULT xy = screen2windowCoordinates( hwnd, lParam );
|
||||||
|
|
||||||
|
// send message
|
||||||
|
::SendMessage( hwnd, uMsg, 0, xy );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the window's system menu.
|
* Opens the window's system menu.
|
||||||
* The system menu is the menu that opens when the user presses Alt+Space or
|
* The system menu is the menu that opens when the user presses Alt+Space or
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ private:
|
|||||||
LRESULT WmNcCalcSize( 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 );
|
LRESULT WmNcHitTest( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam );
|
||||||
|
|
||||||
|
LRESULT screen2windowCoordinates( HWND hwnd, LPARAM lParam );
|
||||||
int getResizeHandleHeight();
|
int getResizeHandleHeight();
|
||||||
bool hasAutohideTaskbar( UINT edge, RECT rcMonitor );
|
bool hasAutohideTaskbar( UINT edge, RECT rcMonitor );
|
||||||
BOOL isFullscreen();
|
BOOL isFullscreen();
|
||||||
@@ -61,6 +62,7 @@ private:
|
|||||||
void fireStateChangedLaterOnce();
|
void fireStateChangedLaterOnce();
|
||||||
JNIEnv* getEnv();
|
JNIEnv* getEnv();
|
||||||
|
|
||||||
|
void sendMessageToClientArea( HWND hwnd, int uMsg, LPARAM lParam );
|
||||||
void openSystemMenu( HWND hwnd, int x, int y );
|
void openSystemMenu( HWND hwnd, int x, int y );
|
||||||
void setMenuItemState( HMENU systemMenu, int item, bool enabled );
|
void setMenuItemState( HMENU systemMenu, int item, bool enabled );
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ extern "C" {
|
|||||||
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTCAPTION 2L
|
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTCAPTION 2L
|
||||||
#undef com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTSYSMENU
|
#undef com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTSYSMENU
|
||||||
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTSYSMENU 3L
|
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTSYSMENU 3L
|
||||||
|
#undef com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTMAXBUTTON
|
||||||
|
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTMAXBUTTON 9L
|
||||||
#undef com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTTOP
|
#undef com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTTOP
|
||||||
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTTOP 12L
|
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTTOP 12L
|
||||||
/*
|
/*
|
||||||
|
|||||||
Reference in New Issue
Block a user