From 4ab90065dc94be5f8861848f4d55d5c2569e7c94 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Sat, 31 Jul 2021 18:02:10 +0200 Subject: [PATCH] Native window decorations: when resizing a window to the right or to the bottom, then first fill the new space with the window background color (instead of black) before the layout is updated (issue #339) --- CHANGELOG.md | 9 ++-- .../ui/FlatWindowsNativeWindowBorder.java | 21 ++++++++ .../FlatWindowsNativeWindowBorder.java | 53 ++++++++++++++++++- .../flatlaf-natives-windows/build.gradle.kts | 4 +- .../src/main/cpp/FlatWndProc.cpp | 18 +++++++ .../src/main/cpp/FlatWndProc.h | 1 + ...ui_FlatWindowsNativeWindowBorder_WndProc.h | 8 +++ 7 files changed, 108 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ab214e2..e5c6f26d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,11 @@ FlatLaf Change Log honor maximum size of internal frame. (issue #362) - Popup: Fixed incorrectly placed drop shadow for medium-weight popups in maximized windows. (issue #358) -- Native window decorations: Fixed occasional application crash on Windows 10 in - `flatlaf-windows.dll`. (issue #357) +- Native window decorations (Windows 10 only): + - Fixed occasional application crash in `flatlaf-windows.dll`. (issue #357) + - When resizing a window to the right or to the bottom, then first fill the + new space with the window background color (instead of black) before the + layout is updated. ## 1.4 @@ -25,7 +28,7 @@ FlatLaf Change Log - Table and PopupFactory: Use `StackWalker` in Java 9+ for better performance. (issue #334) - ToolBar: Paint focus indicator for focused button in toolbar. (issue #346) -- ToolBar: Support focusable buttons in toolbar (set UI values +- ToolBar: Support focusable buttons in toolbar (set UI value `ToolBar.focusableButtons` to `true`). (issue #346) #### Fixed bugs 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 458877bd..9bcca253 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 @@ -25,6 +25,8 @@ import java.awt.Point; import java.awt.Rectangle; import java.awt.Window; import java.awt.geom.AffineTransform; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; @@ -289,6 +291,7 @@ class FlatWindowsNativeWindowBorder //---- class WndProc ------------------------------------------------------ private class WndProc + implements PropertyChangeListener { // WM_NCHITTEST mouse position codes private static final int @@ -313,18 +316,36 @@ class FlatWindowsNativeWindowBorder // remove the OS window title bar updateFrame( hwnd, (window instanceof JFrame) ? ((JFrame)window).getExtendedState() : 0 ); + + // set window background (used when resizing window) + updateWindowBackground(); + window.addPropertyChangeListener( "background", this ); } void uninstall() { + window.removePropertyChangeListener( "background", this ); + uninstallImpl( hwnd ); // cleanup window = null; } + @Override + public void propertyChange( PropertyChangeEvent e ) { + updateWindowBackground(); + } + + private void updateWindowBackground() { + Color bg = window.getBackground(); + if( bg != null ) + setWindowBackground( hwnd, bg.getRed(), bg.getGreen(), bg.getBlue() ); + } + private native long installImpl( Window window ); private native void uninstallImpl( long hwnd ); private native void updateFrame( long hwnd, int state ); + private native void setWindowBackground( long hwnd, int r, int g, int b ); private native void showWindow( long hwnd, int cmd ); // invoked from native code 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 148c204e..c0483051 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 @@ -28,6 +28,8 @@ import java.awt.Point; import java.awt.Rectangle; import java.awt.Window; import java.awt.geom.AffineTransform; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; @@ -47,6 +49,7 @@ 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.ULONG_PTR; +import com.sun.jna.platform.win32.GDI32; import com.sun.jna.platform.win32.Shell32; import com.sun.jna.platform.win32.User32; import com.sun.jna.platform.win32.WTypes.LPWSTR; @@ -57,6 +60,7 @@ import com.sun.jna.platform.win32.WinDef.LRESULT; import com.sun.jna.platform.win32.WinDef.RECT; import com.sun.jna.platform.win32.WinDef.UINT_PTR; import com.sun.jna.platform.win32.WinDef.WPARAM; +import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.platform.win32.WinUser.HMONITOR; import com.sun.jna.platform.win32.WinUser.WindowProc; import com.sun.jna.win32.W32APIOptions; @@ -282,7 +286,7 @@ public class FlatWindowsNativeWindowBorder //---- class WndProc ------------------------------------------------------ private class WndProc - implements WindowProc + implements WindowProc, PropertyChangeListener { private static final int GWLP_WNDPROC = -4; @@ -345,9 +349,15 @@ public class FlatWindowsNativeWindowBorder // remove the OS window title bar updateFrame( (window instanceof JFrame) ? ((JFrame)window).getExtendedState() : 0 ); + + // set window background (used when resizing window) + updateWindowBackground(); + window.addPropertyChangeListener( "background", this ); } void uninstall() { + window.removePropertyChangeListener( "background", this ); + // restore original window procedure if( SystemInfo.isX86_64 ) User32Ex.INSTANCE.SetWindowLongPtr( hwnd, GWLP_WNDPROC, defaultWndProc ); @@ -382,6 +392,28 @@ public class FlatWindowsNativeWindowBorder wmSizeWParam = -1; } + @Override + public void propertyChange( PropertyChangeEvent evt ) { + updateWindowBackground(); + } + + private void updateWindowBackground() { + Color bg = window.getBackground(); + if( bg != null ) + setWindowBackground( bg.getRed(), bg.getGreen(), bg.getBlue() ); + } + + private void setWindowBackground( int r, int g, int b ) { + // delete old background brush + ULONG_PTR oldBrush = User32.INSTANCE.GetClassLongPtr( hwnd, GCLP_HBRBACKGROUND ); + if( oldBrush != null && oldBrush.longValue() != 0 ) + GDI32.INSTANCE.DeleteObject( new HANDLE( oldBrush.toPointer() ) ); + + // create new background brush + HBRUSH brush = GDI32Ex.INSTANCE.CreateSolidBrush( RGB( r, g, b ) ); + User32Ex.INSTANCE.SetClassLongPtr( hwnd, GCLP_HBRBACKGROUND, brush ); + } + /** * NOTE: This method is invoked on the AWT-Windows thread (not the AWT-EventQueue thread). */ @@ -637,6 +669,13 @@ public class FlatWindowsNativeWindowBorder return (short) ((lParam.longValue() >> 16) & 0xffff); } + /** + * Same implementation as RGB(r,g,b) macro in wingdi.h. + */ + private DWORD RGB( int r, int g, int b ) { + return new DWORD( (r & 0xff) | ((g & 0xff) << 8) | ((b & 0xff) << 16) ); + } + /** * Opens the window's system menu. * The system menu is the menu that opens when the user presses Alt+Space or @@ -690,6 +729,8 @@ public class FlatWindowsNativeWindowBorder LONG_PTR SetWindowLong( HWND hWnd, int nIndex, LONG_PTR wndProc ); LRESULT CallWindowProc( LONG_PTR lpPrevWndFunc, HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam ); + LONG_PTR SetClassLongPtr( HWND hWnd, int nIndex, HANDLE wndProc ); + int GetDpiForWindow( HWND hwnd ); int GetSystemMetricsForDpi( int nIndex, int dpi ); @@ -702,6 +743,16 @@ public class FlatWindowsNativeWindowBorder BOOL TrackPopupMenu( HMENU hMenu, int uFlags, int x, int y, int nReserved, HWND hWnd, RECT prcRect ); } + //---- interface GDI32Ex -------------------------------------------------- + + private interface GDI32Ex + extends GDI32 + { + GDI32Ex INSTANCE = Native.load( "gdi32", GDI32Ex.class, W32APIOptions.DEFAULT_OPTIONS ); + + HBRUSH CreateSolidBrush( DWORD color ); + } + //---- class NCCALCSIZE_PARAMS -------------------------------------------- @FieldOrder( { "rgrc" } ) diff --git a/flatlaf-natives/flatlaf-natives-windows/build.gradle.kts b/flatlaf-natives/flatlaf-natives-windows/build.gradle.kts index 0dfa7b08..ba831e20 100644 --- a/flatlaf-natives/flatlaf-natives-windows/build.gradle.kts +++ b/flatlaf-natives/flatlaf-natives-windows/build.gradle.kts @@ -73,8 +73,8 @@ library { linkerArgs.addAll( toolChain.map { when( it ) { - is Gcc, is Clang -> listOf( "-l${jawt}", "-lUser32", "-lshell32", "-lAdvAPI32", "-lKernel32" ) - is VisualCpp -> listOf( "${jawt}.lib", "User32.lib", "shell32.lib", "AdvAPI32.lib", "Kernel32.lib", "/NODEFAULTLIB" ) + is Gcc, is Clang -> listOf( "-l${jawt}", "-lUser32", "-lGdi32", "-lshell32", "-lAdvAPI32", "-lKernel32" ) + is VisualCpp -> listOf( "${jawt}.lib", "User32.lib", "Gdi32.lib", "shell32.lib", "AdvAPI32.lib", "Kernel32.lib", "/NODEFAULTLIB" ) else -> emptyList() } } ) 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 e34dbdee..e919113d 100644 --- a/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.cpp +++ b/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.cpp @@ -52,6 +52,13 @@ JNIEXPORT void JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder FlatWndProc::updateFrame( reinterpret_cast( hwnd ), state ); } +extern "C" +JNIEXPORT void JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_00024WndProc_setWindowBackground + ( JNIEnv* env, jobject obj, jlong hwnd, jint r, jint g, jint b ) +{ + FlatWndProc::setWindowBackground( reinterpret_cast( hwnd ), r, g, b ); +} + extern "C" JNIEXPORT void JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_00024WndProc_showWindow ( JNIEnv* env, jobject obj, jlong hwnd, jint cmd ) @@ -174,6 +181,17 @@ void FlatWndProc::updateFrame( HWND hwnd, int state ) { fwp->wmSizeWParam = -1; } +void FlatWndProc::setWindowBackground( HWND hwnd, int r, int g, int b ) { + // delete old background brush + HBRUSH oldBrush = (HBRUSH) ::GetClassLongPtr( hwnd, GCLP_HBRBACKGROUND ); + if( oldBrush != NULL ) + ::DeleteObject( oldBrush ); + + // create new background brush + HBRUSH brush = ::CreateSolidBrush( RGB( r, g, b ) ); + ::SetClassLongPtr( hwnd, GCLP_HBRBACKGROUND, (LONG_PTR) brush ); +} + LRESULT CALLBACK FlatWndProc::StaticWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { FlatWndProc* fwp = (FlatWndProc*) hwndMap->get( hwnd ); if( fwp == NULL ) 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 52d68789..e4b50f04 100644 --- a/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.h +++ b/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.h @@ -26,6 +26,7 @@ public: static HWND install( JNIEnv *env, jobject obj, jobject window ); static void uninstall( JNIEnv *env, jobject obj, HWND hwnd ); static void updateFrame( HWND hwnd, int state ); + static void setWindowBackground( HWND hwnd, int r, int g, int b ); private: static int initialized; 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 7e586697..8f5e28e0 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 @@ -39,6 +39,14 @@ JNIEXPORT void JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder JNIEXPORT void JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_00024WndProc_updateFrame (JNIEnv *, jobject, jlong, jint); +/* + * Class: com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc + * Method: setWindowBackground + * Signature: (JIII)V + */ +JNIEXPORT void JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_00024WndProc_setWindowBackground + (JNIEnv *, jobject, jlong, jint, jint, jint); + /* * Class: com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc * Method: showWindow