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 260b3294..01665d3a 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 @@ -196,7 +196,9 @@ public class FlatNativeWindowBorder nativeProvider.setHasCustomDecoration( window, hasCustomDecoration ); } - static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight, List hitTestSpots ) { + static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight, + List hitTestSpots, Rectangle appIconBounds ) + { if( canUseJBRCustomDecorations ) { JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots ); return; @@ -207,6 +209,7 @@ public class FlatNativeWindowBorder nativeProvider.setTitleBarHeight( window, titleBarHeight ); nativeProvider.setTitleBarHitTestSpots( window, hitTestSpots ); + nativeProvider.setTitleBarAppIconBounds( window, appIconBounds ); } private static void initialize() { @@ -240,5 +243,6 @@ public class FlatNativeWindowBorder void setHasCustomDecoration( Window window, boolean hasCustomDecoration ); void setTitleBarHeight( Window window, int titleBarHeight ); void setTitleBarHitTestSpots( Window window, List hitTestSpots ); + void setTitleBarAppIconBounds( Window window, Rectangle appIconBounds ); } } 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 dfec73d2..4c969409 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 @@ -453,6 +453,12 @@ public class FlatTitlePane for( Rectangle r : debugHitTestSpots ) g.drawRect( r.x - offset.x, r.y - offset.y, r.width - 1, r.height - 1 ); } + if( debugAppIconBounds != null ) { + g.setColor( Color.red ); + 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 ); + } } debug*/ @@ -632,16 +638,31 @@ debug*/ titleBarHeight--; List hitTestSpots = new ArrayList<>(); - if( iconLabel.isVisible() ) - addNativeHitTestSpot( iconLabel, false, hitTestSpots ); + Rectangle appIconBounds = null; + if( iconLabel.isVisible() ) { + // compute real icon size (without insets) + Point location = SwingUtilities.convertPoint( iconLabel, 0, 0, window ); + Insets iconInsets = iconLabel.getInsets(); + Rectangle iconBounds = new Rectangle( + location.x + iconInsets.left, + location.y + iconInsets.top, + iconLabel.getWidth() - iconInsets.left - iconInsets.right, + iconLabel.getHeight() - iconInsets.top - iconInsets.bottom ); + + if( hasJBRCustomDecoration() ) + hitTestSpots.add( iconBounds ); + else + appIconBounds = iconBounds; + } addNativeHitTestSpot( buttonPanel, false, hitTestSpots ); addNativeHitTestSpot( menuBarPlaceholder, true, hitTestSpots ); - FlatNativeWindowBorder.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots ); + FlatNativeWindowBorder.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots, appIconBounds ); /*debug debugTitleBarHeight = titleBarHeight; debugHitTestSpots = hitTestSpots; + debugAppIconBounds = appIconBounds; repaint(); debug*/ } @@ -663,6 +684,7 @@ debug*/ /*debug private int debugTitleBarHeight; private List debugHitTestSpots; + private Rectangle debugAppIconBounds; debug*/ //---- class TitlePaneBorder ---------------------------------------------- diff --git a/flatlaf-native-jna/src/main/java/com/formdev/flatlaf/nativejna/windows/FlatWindowsNativeWindowBorder.java b/flatlaf-native-jna/src/main/java/com/formdev/flatlaf/nativejna/windows/FlatWindowsNativeWindowBorder.java index 8612079f..617adbf9 100644 --- a/flatlaf-native-jna/src/main/java/com/formdev/flatlaf/nativejna/windows/FlatWindowsNativeWindowBorder.java +++ b/flatlaf-native-jna/src/main/java/com/formdev/flatlaf/nativejna/windows/FlatWindowsNativeWindowBorder.java @@ -157,6 +157,15 @@ public class FlatWindowsNativeWindowBorder 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; + } + //---- class WndProc ------------------------------------------------------ private class WndProc @@ -164,14 +173,16 @@ public class FlatWindowsNativeWindowBorder { private static final int GWLP_WNDPROC = -4; - private static final int WM_NCCALCSIZE = 0x0083; - private static final int WM_NCHITTEST = 0x0084; - private static final int WM_NCRBUTTONUP = 0x00A5; + private static final int + WM_NCCALCSIZE = 0x0083, + WM_NCHITTEST = 0x0084, + WM_NCRBUTTONUP = 0x00A5; // WM_NCHITTEST mouse position codes private static final int HTCLIENT = 1, HTCAPTION = 2, + HTSYSMENU = 3, HTTOP = 12; private static final int ABS_AUTOHIDE = 0x0000001; @@ -198,6 +209,7 @@ public class FlatWindowsNativeWindowBorder private int titleBarHeight; private Rectangle[] hitTestSpots; + private Rectangle appIconBounds; WndProc( Window window ) { this.window = window; @@ -242,7 +254,7 @@ public class FlatWindowsNativeWindowBorder return WmNcHitTest( hwnd, uMsg, wParam, lParam ); case WM_NCRBUTTONUP: - if( wParam.longValue() == HTCAPTION ) + if( wParam.longValue() == HTCAPTION || wParam.longValue() == HTSYSMENU ) openSystemMenu( hwnd, GET_X_LPARAM( lParam ), GET_Y_LPARAM( lParam ) ); break; @@ -367,6 +379,12 @@ public class FlatWindowsNativeWindowBorder 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 new LRESULT( HTSYSMENU ); + int resizeBorderHeight = getResizeHandleHeight(); boolean isOnResizeBorder = (y < resizeBorderHeight) && (User32.INSTANCE.GetWindowLong( hwnd, GWL_STYLE ) & WS_THICKFRAME) != 0; @@ -415,7 +433,7 @@ public class FlatWindowsNativeWindowBorder /** * Scales down in the same way as AWT. - * See AwtWin32GraphicsDevice::ScaleDownX() + * See AwtWin32GraphicsDevice::ScaleDownX() and ::ScaleDownY() */ private Point scaleDown( int x, int y ) { GraphicsConfiguration gc = window.getGraphicsConfiguration(); diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatNativeWindowBorderTest.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatNativeWindowBorderTest.java index 0184cdf1..792695be 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatNativeWindowBorderTest.java +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatNativeWindowBorderTest.java @@ -28,8 +28,8 @@ import java.util.WeakHashMap; import javax.swing.*; import com.formdev.flatlaf.FlatLightLaf; import com.formdev.flatlaf.extras.FlatInspector; -import com.formdev.flatlaf.nativejna.windows.FlatWindowsNativeWindowBorder; import com.formdev.flatlaf.ui.FlatLineBorder; +import com.formdev.flatlaf.ui.FlatNativeWindowBorder; import net.miginfocom.swing.*; /** @@ -258,7 +258,7 @@ public class FlatNativeWindowBorderTest } private void nativeChanged() { - FlatWindowsNativeWindowBorder.getInstance().setHasCustomDecoration( window, nativeCheckBox.isSelected() ); + FlatNativeWindowBorder.setHasCustomDecoration( window, nativeCheckBox.isSelected() ); } private void revalidateLayout() {