From 54e6cefa67228282871abe17026d5e76ee59afdd Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Tue, 9 Nov 2021 15:36:21 +0100 Subject: [PATCH] Native window decorations: show Windows 11 snap layouts menu when hovering the mouse over the maximize button (issues #397 and #407) --- CHANGELOG.md | 2 + .../flatlaf/ui/FlatNativeWindowBorder.java | 11 +- .../com/formdev/flatlaf/ui/FlatTitlePane.java | 21 +++- .../ui/FlatWindowsNativeWindowBorder.java | 36 +++--- .../flatlaf-natives-jna/build.gradle.kts | 4 +- .../FlatWindowsNativeWindowBorder.java | 114 +++++++++++++----- .../src/main/cpp/FlatWndProc.cpp | 52 +++++++- .../src/main/cpp/FlatWndProc.h | 2 + ...ui_FlatWindowsNativeWindowBorder_WndProc.h | 2 + 9 files changed, 180 insertions(+), 64 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7a966db..f7477fdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ FlatLaf Change Log - Style classes allow defining style rules at a single place (in UI defaults) and use them in any component. (PR #388)\ 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 icons (set client property `JTextField.leadingIcon` or `JTextField.trailingIcon` to an `Icon`). (PR #378; issue #368) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeWindowBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeWindowBorder.java index 1042c52c..2197c46a 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeWindowBorder.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeWindowBorder.java @@ -235,7 +235,7 @@ public class FlatNativeWindowBorder } static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight, - List hitTestSpots, Rectangle appIconBounds ) + List hitTestSpots, Rectangle appIconBounds, Rectangle maximizeButtonBounds ) { if( canUseJBRCustomDecorations ) { JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots ); @@ -245,9 +245,7 @@ public class FlatNativeWindowBorder if( !isSupported() ) return; - nativeProvider.setTitleBarHeight( window, titleBarHeight ); - nativeProvider.setTitleBarHitTestSpots( window, hitTestSpots ); - nativeProvider.setTitleBarAppIconBounds( window, appIconBounds ); + nativeProvider.updateTitleBarInfo( window, titleBarHeight, hitTestSpots, appIconBounds, maximizeButtonBounds ); } static boolean showWindow( Window window, int cmd ) { @@ -292,9 +290,8 @@ public class FlatNativeWindowBorder { boolean hasCustomDecoration( Window window ); void setHasCustomDecoration( Window window, boolean hasCustomDecoration ); - void setTitleBarHeight( Window window, int titleBarHeight ); - void setTitleBarHitTestSpots( Window window, List hitTestSpots ); - void setTitleBarAppIconBounds( Window window, Rectangle appIconBounds ); + void updateTitleBarInfo( Window window, int titleBarHeight, List hitTestSpots, + Rectangle appIconBounds, Rectangle maximizeButtonBounds ); // commands for showWindow(); values must match Win32 API // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java index 89c323c9..9a637ce4 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java @@ -527,11 +527,17 @@ public class FlatTitlePane g.drawRect( r.x - offset.x, r.y - offset.y, r.width - 1, r.height - 1 ); } if( debugAppIconBounds != null ) { - g.setColor( Color.blue); + g.setColor( Color.blue ); Point offset = SwingUtilities.convertPoint( this, 0, 0, window ); Rectangle r = debugAppIconBounds; 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*/ @@ -722,6 +728,7 @@ debug*/ List hitTestSpots = new ArrayList<>(); Rectangle appIconBounds = null; + if( iconLabel.isVisible() ) { // compute real icon size (without insets; 1px wider for easier hitting) Point location = SwingUtilities.convertPoint( iconLabel, 0, 0, window ); @@ -753,6 +760,13 @@ debug*/ 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 ); if( r != null ) hitTestSpots.add( r ); @@ -787,12 +801,14 @@ debug*/ } } - FlatNativeWindowBorder.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots, appIconBounds ); + FlatNativeWindowBorder.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, + hitTestSpots, appIconBounds, maximizeButtonBounds ); /*debug debugTitleBarHeight = titleBarHeight; debugHitTestSpots = hitTestSpots; debugAppIconBounds = appIconBounds; + debugMaximizeButtonBounds = maximizeButtonBounds; repaint(); debug*/ } @@ -815,6 +831,7 @@ debug*/ private int debugTitleBarHeight; private List debugHitTestSpots; private Rectangle debugAppIconBounds; + private Rectangle debugMaximizeButtonBounds; debug*/ //---- class FlatTitlePaneBorder ------------------------------------------ diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatWindowsNativeWindowBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatWindowsNativeWindowBorder.java index 311e46c3..0ee34766 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatWindowsNativeWindowBorder.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatWindowsNativeWindowBorder.java @@ -54,6 +54,10 @@ import com.formdev.flatlaf.util.SystemInfo; // https://github.com/oberth/custom-chrome // 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. @@ -177,30 +181,17 @@ class FlatWindowsNativeWindowBorder } @Override - public void setTitleBarHeight( Window window, int titleBarHeight ) { + public void updateTitleBarInfo( Window window, int titleBarHeight, List hitTestSpots, + Rectangle appIconBounds, Rectangle maximizeButtonBounds ) + { WndProc wndProc = windowsMap.get( window ); if( wndProc == null ) return; wndProc.titleBarHeight = titleBarHeight; - } - - @Override - public void setTitleBarHitTestSpots( Window window, List hitTestSpots ) { - WndProc wndProc = windowsMap.get( window ); - if( wndProc == null ) - return; - wndProc.hitTestSpots = hitTestSpots.toArray( new Rectangle[hitTestSpots.size()] ); - } - - @Override - public void setTitleBarAppIconBounds( Window window, Rectangle appIconBounds ) { - WndProc wndProc = windowsMap.get( window ); - if( wndProc == null ) - return; - wndProc.appIconBounds = (appIconBounds != null) ? new Rectangle( appIconBounds ) : null; + wndProc.maximizeButtonBounds = (maximizeButtonBounds != null) ? new Rectangle( maximizeButtonBounds ) : null; } @Override @@ -303,14 +294,17 @@ class FlatWindowsNativeWindowBorder HTCLIENT = 1, HTCAPTION = 2, HTSYSMENU = 3, + HTMAXBUTTON = 9, HTTOP = 12; private Window window; private final long hwnd; + // Swing coordinates/values may be scaled on a HiDPI screen private int titleBarHeight; private Rectangle[] hitTestSpots; private Rectangle appIconBounds; + private Rectangle maximizeButtonBounds; WndProc( Window window ) { this.window = window; @@ -355,7 +349,7 @@ class FlatWindowsNativeWindowBorder // invoked from native code 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 ); int sx = pt.x; int sy = pt.y; @@ -366,6 +360,12 @@ class FlatWindowsNativeWindowBorder if( appIconBounds != null && appIconBounds.contains( sx, sy ) ) 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); if( isOnTitleBar ) { diff --git a/flatlaf-natives/flatlaf-natives-jna/build.gradle.kts b/flatlaf-natives/flatlaf-natives-jna/build.gradle.kts index 92ebbe86..4cade0e8 100644 --- a/flatlaf-natives/flatlaf-natives-jna/build.gradle.kts +++ b/flatlaf-natives/flatlaf-natives-jna/build.gradle.kts @@ -20,6 +20,6 @@ plugins { dependencies { implementation( project( ":flatlaf-core" ) ) - implementation( "net.java.dev.jna:jna:5.7.0" ) - implementation( "net.java.dev.jna:jna-platform:5.7.0" ) + implementation( "net.java.dev.jna:jna:5.10.0" ) + implementation( "net.java.dev.jna:jna-platform:5.10.0" ) } diff --git a/flatlaf-natives/flatlaf-natives-jna/src/main/java/com/formdev/flatlaf/natives/jna/windows/FlatWindowsNativeWindowBorder.java b/flatlaf-natives/flatlaf-natives-jna/src/main/java/com/formdev/flatlaf/natives/jna/windows/FlatWindowsNativeWindowBorder.java index 59180560..fe8e21b6 100644 --- a/flatlaf-natives/flatlaf-natives-jna/src/main/java/com/formdev/flatlaf/natives/jna/windows/FlatWindowsNativeWindowBorder.java +++ b/flatlaf-natives/flatlaf-natives-jna/src/main/java/com/formdev/flatlaf/natives/jna/windows/FlatWindowsNativeWindowBorder.java @@ -47,7 +47,7 @@ import com.sun.jna.Pointer; import com.sun.jna.Structure; import com.sun.jna.Structure.FieldOrder; 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.GDI32; 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/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. @@ -160,30 +164,17 @@ public class FlatWindowsNativeWindowBorder } @Override - public void setTitleBarHeight( Window window, int titleBarHeight ) { + public void updateTitleBarInfo( Window window, int titleBarHeight, List hitTestSpots, + Rectangle appIconBounds, Rectangle maximizeButtonBounds ) + { WndProc wndProc = windowsMap.get( window ); if( wndProc == null ) return; wndProc.titleBarHeight = titleBarHeight; - } - - @Override - public void setTitleBarHitTestSpots( Window window, List hitTestSpots ) { - WndProc wndProc = windowsMap.get( window ); - if( wndProc == null ) - return; - wndProc.hitTestSpots = hitTestSpots.toArray( new Rectangle[hitTestSpots.size()] ); - } - - @Override - public void setTitleBarAppIconBounds( Window window, Rectangle appIconBounds ) { - WndProc wndProc = windowsMap.get( window ); - if( wndProc == null ) - return; - wndProc.appIconBounds = (appIconBounds != null) ? new Rectangle( appIconBounds ) : null; + wndProc.maximizeButtonBounds = (maximizeButtonBounds != null) ? new Rectangle( maximizeButtonBounds ) : null; } @Override @@ -293,7 +284,16 @@ public class FlatWindowsNativeWindowBorder WM_ERASEBKGND = 0x0014, WM_NCCALCSIZE = 0x0083, WM_NCHITTEST = 0x0084, + + WM_NCMOUSEMOVE = 0x00A0, + WM_NCLBUTTONDOWN = 0x00A1, + WM_NCLBUTTONUP = 0x00A2, WM_NCRBUTTONUP = 0x00A5, + + WM_MOUSEMOVE= 0x0200, + WM_LBUTTONDOWN = 0x0201, + WM_LBUTTONUP = 0x0202, + WM_DWMCOLORIZATIONCOLORCHANGED = 0x0320; // WM_SIZE wParam @@ -306,6 +306,7 @@ public class FlatWindowsNativeWindowBorder HTCLIENT = 1, HTCAPTION = 2, HTSYSMENU = 3, + HTMAXBUTTON = 9, HTTOP = 12; private static final int ABS_AUTOHIDE = 0x0000001; @@ -328,13 +329,15 @@ public class FlatWindowsNativeWindowBorder private Window window; private final HWND hwnd; - private final BaseTSD.LONG_PTR defaultWndProc; + private final LONG_PTR defaultWndProc; private int wmSizeWParam = -1; private HBRUSH background; + // Swing coordinates/values may be scaled on a HiDPI screen private int titleBarHeight; private Rectangle[] hitTestSpots; private Rectangle appIconBounds; + private Rectangle maximizeButtonBounds; WndProc( Window window ) { this.window = window; @@ -427,6 +430,26 @@ public class FlatWindowsNativeWindowBorder case WM_NCHITTEST: 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: if( wParam.longValue() == HTCAPTION || wParam.longValue() == HTSYSMENU ) openSystemMenu( hwnd, GET_X_LPARAM( lParam ), GET_Y_LPARAM( lParam ) ); @@ -522,7 +545,7 @@ public class FlatWindowsNativeWindowBorder boolean isMaximized = User32Ex.INSTANCE.IsZoomed( hwnd ); 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 // 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 @@ -574,15 +597,12 @@ public class FlatWindowsNativeWindowBorder if( lResult.longValue() != HTCLIENT ) 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 - int x = GET_X_LPARAM( lParam ) - rcWindow.left; - int y = GET_Y_LPARAM( lParam ) - rcWindow.top; + LRESULT xy = screen2windowCoordinates( hwnd, lParam ); + 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 ); int sx = pt.x; int sy = pt.y; @@ -593,6 +613,12 @@ public class FlatWindowsNativeWindowBorder if( appIconBounds != null && appIconBounds.contains( sx, sy ) ) 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(); boolean isOnResizeBorder = (y < resizeBorderHeight) && (User32.INSTANCE.GetWindowLong( hwnd, GWL_STYLE ) & WS_THICKFRAME) != 0; @@ -612,6 +638,21 @@ public class FlatWindowsNativeWindowBorder 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 * resize the window. @@ -678,7 +719,7 @@ public class FlatWindowsNativeWindowBorder * * 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); } @@ -688,10 +729,17 @@ public class FlatWindowsNativeWindowBorder * * 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); } + /** + * 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. */ @@ -699,6 +747,14 @@ public class FlatWindowsNativeWindowBorder 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. * The system menu is the menu that opens when the user presses Alt+Space or diff --git a/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.cpp b/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.cpp index 0e33534c..f745917b 100644 --- a/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.cpp +++ b/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.cpp @@ -215,6 +215,26 @@ LRESULT CALLBACK FlatWndProc::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, L case WM_NCHITTEST: 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: if( wParam == HTCAPTION || wParam == HTSYSMENU ) 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 ); 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 // 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 @@ -354,6 +374,22 @@ LRESULT FlatWndProc::WmNcHitTest( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lPa if( lResult != HTCLIENT ) 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 RECT 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 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 ); + return MAKELONG( x, y ); } /** @@ -429,6 +461,14 @@ JNIEnv* FlatWndProc::getEnv() { 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. * The system menu is the menu that opens when the user presses Alt+Space or diff --git a/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.h b/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.h index 900286c0..646f5a57 100644 --- a/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.h +++ b/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.h @@ -54,6 +54,7 @@ private: LRESULT WmNcCalcSize( 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(); bool hasAutohideTaskbar( UINT edge, RECT rcMonitor ); BOOL isFullscreen(); @@ -61,6 +62,7 @@ private: void fireStateChangedLaterOnce(); JNIEnv* getEnv(); + void sendMessageToClientArea( HWND hwnd, int uMsg, LPARAM lParam ); void openSystemMenu( HWND hwnd, int x, int y ); void setMenuItemState( HMENU systemMenu, int item, bool enabled ); diff --git a/flatlaf-natives/flatlaf-natives-windows/src/main/headers/com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc.h b/flatlaf-natives/flatlaf-natives-windows/src/main/headers/com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc.h index 8f5e28e0..65c9c35a 100644 --- a/flatlaf-natives/flatlaf-natives-windows/src/main/headers/com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc.h +++ b/flatlaf-natives/flatlaf-natives-windows/src/main/headers/com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc.h @@ -13,6 +13,8 @@ extern "C" { #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_HTMAXBUTTON +#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTMAXBUTTON 9L #undef com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTTOP #define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTTOP 12L /*