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:
Karl Tauber
2021-11-09 15:36:21 +01:00
parent 33b25c1129
commit 54e6cefa67
9 changed files with 180 additions and 64 deletions

View File

@@ -235,7 +235,7 @@ public class FlatNativeWindowBorder
}
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight,
List<Rectangle> hitTestSpots, Rectangle appIconBounds )
List<Rectangle> 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<Rectangle> hitTestSpots );
void setTitleBarAppIconBounds( Window window, Rectangle appIconBounds );
void updateTitleBarInfo( Window window, int titleBarHeight, List<Rectangle> 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

View File

@@ -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<Rectangle> 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<Rectangle> debugHitTestSpots;
private Rectangle debugAppIconBounds;
private Rectangle debugMaximizeButtonBounds;
debug*/
//---- class FlatTitlePaneBorder ------------------------------------------

View File

@@ -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<Rectangle> hitTestSpots,
Rectangle appIconBounds, Rectangle maximizeButtonBounds )
{
WndProc wndProc = windowsMap.get( window );
if( wndProc == null )
return;
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()] );
}
@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 ) {