From 7f02eb9cf0580f8464ccdc41798244f1ea8e50e1 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Sat, 31 Jul 2021 21:02:42 +0200 Subject: [PATCH] Native window decorations: when window is initially shown, fill background with window background color (instead of white), which avoids flickering in dark themes (issue #339) --- CHANGELOG.md | 11 ++++-- .../FlatWindowsNativeWindowBorder.java | 37 ++++++++++++++---- .../src/main/cpp/FlatWndProc.cpp | 38 ++++++++++++++++--- .../src/main/cpp/FlatWndProc.h | 2 + 4 files changed, 73 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5c6f26d..bf2d2cc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,9 +11,14 @@ FlatLaf Change Log maximized windows. (issue #358) - 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. + - When window is initially shown, fill background with window background color + (instead of white), which avoids flickering in dark themes. (issue 339) + - When resizing a window at the right/bottom edge, then first fill the new + space with the window background color (instead of black) before the layout + is updated. + - When resizing a window at the left/top edge, then first fill the new space + with the window background color (instead of garbage) before the layout is + updated. ## 1.4 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 c0483051..59180560 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 @@ -60,7 +60,6 @@ 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; @@ -291,6 +290,7 @@ public class FlatWindowsNativeWindowBorder private static final int GWLP_WNDPROC = -4; private static final int + WM_ERASEBKGND = 0x0014, WM_NCCALCSIZE = 0x0083, WM_NCHITTEST = 0x0084, WM_NCRBUTTONUP = 0x00A5, @@ -330,6 +330,7 @@ public class FlatWindowsNativeWindowBorder private final HWND hwnd; private final BaseTSD.LONG_PTR defaultWndProc; private int wmSizeWParam = -1; + private HBRUSH background; private int titleBarHeight; private Rectangle[] hitTestSpots; @@ -368,6 +369,8 @@ public class FlatWindowsNativeWindowBorder updateFrame( 0 ); // cleanup + if( background != null ) + GDI32.INSTANCE.DeleteObject( background ); window = null; } @@ -405,13 +408,11 @@ public class FlatWindowsNativeWindowBorder 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() ) ); + if( background != null ) + GDI32.INSTANCE.DeleteObject( background ); // create new background brush - HBRUSH brush = GDI32Ex.INSTANCE.CreateSolidBrush( RGB( r, g, b ) ); - User32Ex.INSTANCE.SetClassLongPtr( hwnd, GCLP_HBRBACKGROUND, brush ); + background = GDI32Ex.INSTANCE.CreateSolidBrush( RGB( r, g, b ) ); } /** @@ -440,6 +441,9 @@ public class FlatWindowsNativeWindowBorder wParam = new WPARAM( wmSizeWParam ); break; + case WM_ERASEBKGND: + return WmEraseBkgnd( hwnd, uMsg, wParam, lParam ); + case WM_DESTROY: return WmDestroy( hwnd, uMsg, wParam, lParam ); } @@ -464,11 +468,30 @@ public class FlatWindowsNativeWindowBorder // cleanup windowsMap.remove( window ); + if( background != null ) + GDI32.INSTANCE.DeleteObject( background ); window = null; return lResult; } + /** + * Handle WM_ERASEBKGND + * + * https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-erasebkgnd + */ + LRESULT WmEraseBkgnd( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam ) { + if( background == null ) + return new LRESULT( 0 ); + + // fill background + HDC hdc = new HDC( wParam.toPointer() ); + RECT rect = new RECT(); + User32.INSTANCE.GetClientRect( hwnd, rect ); + User32Ex.INSTANCE.FillRect( hdc, rect, background ); + return new LRESULT( 1 ); + } + /** * Handle WM_NCCALCSIZE * @@ -729,7 +752,7 @@ 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 FillRect( HDC hDC, RECT lprc, HBRUSH hbr ); int GetDpiForWindow( HWND hwnd ); int GetSystemMetricsForDpi( int nIndex, int dpi ); 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 e919113d..0e33534c 100644 --- a/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.cpp +++ b/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.cpp @@ -87,6 +87,7 @@ FlatWndProc::FlatWndProc() { hwnd = NULL; defaultWndProc = NULL; wmSizeWParam = -1; + background = NULL; } HWND FlatWndProc::install( JNIEnv *env, jobject obj, jobject window ) { @@ -135,6 +136,8 @@ void FlatWndProc::uninstall( JNIEnv *env, jobject obj, HWND hwnd ) { // cleanup env->DeleteGlobalRef( fwp->obj ); + if( fwp->background != NULL ) + ::DeleteObject( fwp->background ); delete fwp; } @@ -182,14 +185,16 @@ void FlatWndProc::updateFrame( HWND hwnd, int state ) { } void FlatWndProc::setWindowBackground( HWND hwnd, int r, int g, int b ) { + FlatWndProc* fwp = (FlatWndProc*) hwndMap->get( hwnd ); + if( fwp == NULL ) + return; + // delete old background brush - HBRUSH oldBrush = (HBRUSH) ::GetClassLongPtr( hwnd, GCLP_HBRBACKGROUND ); - if( oldBrush != NULL ) - ::DeleteObject( oldBrush ); + if( fwp->background != NULL ) + ::DeleteObject( fwp->background ); // create new background brush - HBRUSH brush = ::CreateSolidBrush( RGB( r, g, b ) ); - ::SetClassLongPtr( hwnd, GCLP_HBRBACKGROUND, (LONG_PTR) brush ); + fwp->background = ::CreateSolidBrush( RGB( r, g, b ) ); } LRESULT CALLBACK FlatWndProc::StaticWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { @@ -224,12 +229,16 @@ LRESULT CALLBACK FlatWndProc::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, L wParam = wmSizeWParam; break; + case WM_ERASEBKGND: + return WmEraseBkgnd( hwnd, uMsg, wParam, lParam ); + case WM_DESTROY: return WmDestroy( hwnd, uMsg, wParam, lParam ); } return ::CallWindowProc( defaultWndProc, hwnd, uMsg, wParam, lParam ); } + /** * Handle WM_DESTROY * @@ -243,6 +252,8 @@ LRESULT FlatWndProc::WmDestroy( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lPara // cleanup getEnv()->DeleteGlobalRef( obj ); + if( background != NULL ) + ::DeleteObject( background ); hwndMap->remove( hwnd ); delete this; @@ -250,6 +261,23 @@ LRESULT FlatWndProc::WmDestroy( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lPara return ::CallWindowProc( defaultWndProc2, hwnd, uMsg, wParam, lParam ); } +/** + * Handle WM_ERASEBKGND + * + * https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-erasebkgnd + */ +LRESULT FlatWndProc::WmEraseBkgnd( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam ) { + if( background == NULL ) + return FALSE; + + // fill background + HDC hdc = (HDC) wParam; + RECT rect; + ::GetClientRect( hwnd, &rect ); + ::FillRect( hdc, &rect, background ); + return TRUE; +} + /** * Handle WM_NCCALCSIZE * 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 e4b50f04..900286c0 100644 --- a/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.h +++ b/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/FlatWndProc.h @@ -42,6 +42,7 @@ private: HWND hwnd; WNDPROC defaultWndProc; int wmSizeWParam; + HBRUSH background; FlatWndProc(); static void initIDs( JNIEnv *env, jobject obj ); @@ -49,6 +50,7 @@ private: static LRESULT CALLBACK StaticWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); LRESULT WmDestroy( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam ); + LRESULT WmEraseBkgnd( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam ); LRESULT WmNcCalcSize( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam ); LRESULT WmNcHitTest( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam );