Native window decorations: show window system menu when left-clicking on application icon, close window on left-double-click on app icon

This commit is contained in:
Karl Tauber
2021-02-23 23:31:36 +01:00
parent baf4437efc
commit 49bd53194a
4 changed files with 55 additions and 11 deletions

View File

@@ -196,7 +196,9 @@ public class FlatNativeWindowBorder
nativeProvider.setHasCustomDecoration( window, hasCustomDecoration ); nativeProvider.setHasCustomDecoration( window, hasCustomDecoration );
} }
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight, List<Rectangle> hitTestSpots ) { static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight,
List<Rectangle> hitTestSpots, Rectangle appIconBounds )
{
if( canUseJBRCustomDecorations ) { if( canUseJBRCustomDecorations ) {
JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots ); JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots );
return; return;
@@ -207,6 +209,7 @@ public class FlatNativeWindowBorder
nativeProvider.setTitleBarHeight( window, titleBarHeight ); nativeProvider.setTitleBarHeight( window, titleBarHeight );
nativeProvider.setTitleBarHitTestSpots( window, hitTestSpots ); nativeProvider.setTitleBarHitTestSpots( window, hitTestSpots );
nativeProvider.setTitleBarAppIconBounds( window, appIconBounds );
} }
private static void initialize() { private static void initialize() {
@@ -240,5 +243,6 @@ public class FlatNativeWindowBorder
void setHasCustomDecoration( Window window, boolean hasCustomDecoration ); void setHasCustomDecoration( Window window, boolean hasCustomDecoration );
void setTitleBarHeight( Window window, int titleBarHeight ); void setTitleBarHeight( Window window, int titleBarHeight );
void setTitleBarHitTestSpots( Window window, List<Rectangle> hitTestSpots ); void setTitleBarHitTestSpots( Window window, List<Rectangle> hitTestSpots );
void setTitleBarAppIconBounds( Window window, Rectangle appIconBounds );
} }
} }

View File

@@ -453,6 +453,12 @@ public class FlatTitlePane
for( Rectangle r : debugHitTestSpots ) for( Rectangle r : debugHitTestSpots )
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 ) {
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*/ debug*/
@@ -632,16 +638,31 @@ debug*/
titleBarHeight--; titleBarHeight--;
List<Rectangle> hitTestSpots = new ArrayList<>(); List<Rectangle> hitTestSpots = new ArrayList<>();
if( iconLabel.isVisible() ) Rectangle appIconBounds = null;
addNativeHitTestSpot( iconLabel, false, hitTestSpots ); 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( buttonPanel, false, hitTestSpots );
addNativeHitTestSpot( menuBarPlaceholder, true, hitTestSpots ); addNativeHitTestSpot( menuBarPlaceholder, true, hitTestSpots );
FlatNativeWindowBorder.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots ); FlatNativeWindowBorder.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots, appIconBounds );
/*debug /*debug
debugTitleBarHeight = titleBarHeight; debugTitleBarHeight = titleBarHeight;
debugHitTestSpots = hitTestSpots; debugHitTestSpots = hitTestSpots;
debugAppIconBounds = appIconBounds;
repaint(); repaint();
debug*/ debug*/
} }
@@ -663,6 +684,7 @@ debug*/
/*debug /*debug
private int debugTitleBarHeight; private int debugTitleBarHeight;
private List<Rectangle> debugHitTestSpots; private List<Rectangle> debugHitTestSpots;
private Rectangle debugAppIconBounds;
debug*/ debug*/
//---- class TitlePaneBorder ---------------------------------------------- //---- class TitlePaneBorder ----------------------------------------------

View File

@@ -157,6 +157,15 @@ public class FlatWindowsNativeWindowBorder
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;
}
//---- class WndProc ------------------------------------------------------ //---- class WndProc ------------------------------------------------------
private class WndProc private class WndProc
@@ -164,14 +173,16 @@ public class FlatWindowsNativeWindowBorder
{ {
private static final int GWLP_WNDPROC = -4; private static final int GWLP_WNDPROC = -4;
private static final int WM_NCCALCSIZE = 0x0083; private static final int
private static final int WM_NCHITTEST = 0x0084; WM_NCCALCSIZE = 0x0083,
private static final int WM_NCRBUTTONUP = 0x00A5; WM_NCHITTEST = 0x0084,
WM_NCRBUTTONUP = 0x00A5;
// WM_NCHITTEST mouse position codes // WM_NCHITTEST mouse position codes
private static final int private static final int
HTCLIENT = 1, HTCLIENT = 1,
HTCAPTION = 2, HTCAPTION = 2,
HTSYSMENU = 3,
HTTOP = 12; HTTOP = 12;
private static final int ABS_AUTOHIDE = 0x0000001; private static final int ABS_AUTOHIDE = 0x0000001;
@@ -198,6 +209,7 @@ public class FlatWindowsNativeWindowBorder
private int titleBarHeight; private int titleBarHeight;
private Rectangle[] hitTestSpots; private Rectangle[] hitTestSpots;
private Rectangle appIconBounds;
WndProc( Window window ) { WndProc( Window window ) {
this.window = window; this.window = window;
@@ -242,7 +254,7 @@ public class FlatWindowsNativeWindowBorder
return WmNcHitTest( hwnd, uMsg, wParam, lParam ); return WmNcHitTest( hwnd, uMsg, wParam, lParam );
case WM_NCRBUTTONUP: case WM_NCRBUTTONUP:
if( wParam.longValue() == HTCAPTION ) 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 ) );
break; break;
@@ -367,6 +379,12 @@ public class FlatWindowsNativeWindowBorder
int sx = pt.x; int sx = pt.x;
int sy = pt.y; 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(); 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;
@@ -415,7 +433,7 @@ public class FlatWindowsNativeWindowBorder
/** /**
* Scales down in the same way as AWT. * Scales down in the same way as AWT.
* See AwtWin32GraphicsDevice::ScaleDownX() * See AwtWin32GraphicsDevice::ScaleDownX() and ::ScaleDownY()
*/ */
private Point scaleDown( int x, int y ) { private Point scaleDown( int x, int y ) {
GraphicsConfiguration gc = window.getGraphicsConfiguration(); GraphicsConfiguration gc = window.getGraphicsConfiguration();

View File

@@ -28,8 +28,8 @@ import java.util.WeakHashMap;
import javax.swing.*; import javax.swing.*;
import com.formdev.flatlaf.FlatLightLaf; import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.extras.FlatInspector; import com.formdev.flatlaf.extras.FlatInspector;
import com.formdev.flatlaf.nativejna.windows.FlatWindowsNativeWindowBorder;
import com.formdev.flatlaf.ui.FlatLineBorder; import com.formdev.flatlaf.ui.FlatLineBorder;
import com.formdev.flatlaf.ui.FlatNativeWindowBorder;
import net.miginfocom.swing.*; import net.miginfocom.swing.*;
/** /**
@@ -258,7 +258,7 @@ public class FlatNativeWindowBorderTest
} }
private void nativeChanged() { private void nativeChanged() {
FlatWindowsNativeWindowBorder.getInstance().setHasCustomDecoration( window, nativeCheckBox.isSelected() ); FlatNativeWindowBorder.setHasCustomDecoration( window, nativeCheckBox.isSelected() );
} }
private void revalidateLayout() { private void revalidateLayout() {