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)

This commit is contained in:
Karl Tauber
2021-07-31 21:02:42 +02:00
parent 4ab90065dc
commit 7f02eb9cf0
4 changed files with 73 additions and 15 deletions

View File

@@ -11,9 +11,14 @@ FlatLaf Change Log
maximized windows. (issue #358) maximized windows. (issue #358)
- Native window decorations (Windows 10 only): - Native window decorations (Windows 10 only):
- Fixed occasional application crash in `flatlaf-windows.dll`. (issue #357) - 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 - When window is initially shown, fill background with window background color
new space with the window background color (instead of black) before the (instead of white), which avoids flickering in dark themes. (issue 339)
layout is updated. - 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 ## 1.4

View File

@@ -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.RECT;
import com.sun.jna.platform.win32.WinDef.UINT_PTR; import com.sun.jna.platform.win32.WinDef.UINT_PTR;
import com.sun.jna.platform.win32.WinDef.WPARAM; 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.HMONITOR;
import com.sun.jna.platform.win32.WinUser.WindowProc; import com.sun.jna.platform.win32.WinUser.WindowProc;
import com.sun.jna.win32.W32APIOptions; import com.sun.jna.win32.W32APIOptions;
@@ -291,6 +290,7 @@ public class FlatWindowsNativeWindowBorder
private static final int GWLP_WNDPROC = -4; private static final int GWLP_WNDPROC = -4;
private static final int private static final int
WM_ERASEBKGND = 0x0014,
WM_NCCALCSIZE = 0x0083, WM_NCCALCSIZE = 0x0083,
WM_NCHITTEST = 0x0084, WM_NCHITTEST = 0x0084,
WM_NCRBUTTONUP = 0x00A5, WM_NCRBUTTONUP = 0x00A5,
@@ -330,6 +330,7 @@ public class FlatWindowsNativeWindowBorder
private final HWND hwnd; private final HWND hwnd;
private final BaseTSD.LONG_PTR defaultWndProc; private final BaseTSD.LONG_PTR defaultWndProc;
private int wmSizeWParam = -1; private int wmSizeWParam = -1;
private HBRUSH background;
private int titleBarHeight; private int titleBarHeight;
private Rectangle[] hitTestSpots; private Rectangle[] hitTestSpots;
@@ -368,6 +369,8 @@ public class FlatWindowsNativeWindowBorder
updateFrame( 0 ); updateFrame( 0 );
// cleanup // cleanup
if( background != null )
GDI32.INSTANCE.DeleteObject( background );
window = null; window = null;
} }
@@ -405,13 +408,11 @@ public class FlatWindowsNativeWindowBorder
private void setWindowBackground( int r, int g, int b ) { private void setWindowBackground( int r, int g, int b ) {
// delete old background brush // delete old background brush
ULONG_PTR oldBrush = User32.INSTANCE.GetClassLongPtr( hwnd, GCLP_HBRBACKGROUND ); if( background != null )
if( oldBrush != null && oldBrush.longValue() != 0 ) GDI32.INSTANCE.DeleteObject( background );
GDI32.INSTANCE.DeleteObject( new HANDLE( oldBrush.toPointer() ) );
// create new background brush // create new background brush
HBRUSH brush = GDI32Ex.INSTANCE.CreateSolidBrush( RGB( r, g, b ) ); background = GDI32Ex.INSTANCE.CreateSolidBrush( RGB( r, g, b ) );
User32Ex.INSTANCE.SetClassLongPtr( hwnd, GCLP_HBRBACKGROUND, brush );
} }
/** /**
@@ -440,6 +441,9 @@ public class FlatWindowsNativeWindowBorder
wParam = new WPARAM( wmSizeWParam ); wParam = new WPARAM( wmSizeWParam );
break; break;
case WM_ERASEBKGND:
return WmEraseBkgnd( hwnd, uMsg, wParam, lParam );
case WM_DESTROY: case WM_DESTROY:
return WmDestroy( hwnd, uMsg, wParam, lParam ); return WmDestroy( hwnd, uMsg, wParam, lParam );
} }
@@ -464,11 +468,30 @@ public class FlatWindowsNativeWindowBorder
// cleanup // cleanup
windowsMap.remove( window ); windowsMap.remove( window );
if( background != null )
GDI32.INSTANCE.DeleteObject( background );
window = null; window = null;
return lResult; 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 * Handle WM_NCCALCSIZE
* *
@@ -729,7 +752,7 @@ public class FlatWindowsNativeWindowBorder
LONG_PTR SetWindowLong( HWND hWnd, int nIndex, LONG_PTR wndProc ); LONG_PTR SetWindowLong( HWND hWnd, int nIndex, LONG_PTR wndProc );
LRESULT CallWindowProc( LONG_PTR lpPrevWndFunc, HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam ); 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 GetDpiForWindow( HWND hwnd );
int GetSystemMetricsForDpi( int nIndex, int dpi ); int GetSystemMetricsForDpi( int nIndex, int dpi );

View File

@@ -87,6 +87,7 @@ FlatWndProc::FlatWndProc() {
hwnd = NULL; hwnd = NULL;
defaultWndProc = NULL; defaultWndProc = NULL;
wmSizeWParam = -1; wmSizeWParam = -1;
background = NULL;
} }
HWND FlatWndProc::install( JNIEnv *env, jobject obj, jobject window ) { HWND FlatWndProc::install( JNIEnv *env, jobject obj, jobject window ) {
@@ -135,6 +136,8 @@ void FlatWndProc::uninstall( JNIEnv *env, jobject obj, HWND hwnd ) {
// cleanup // cleanup
env->DeleteGlobalRef( fwp->obj ); env->DeleteGlobalRef( fwp->obj );
if( fwp->background != NULL )
::DeleteObject( fwp->background );
delete fwp; delete fwp;
} }
@@ -182,14 +185,16 @@ void FlatWndProc::updateFrame( HWND hwnd, int state ) {
} }
void FlatWndProc::setWindowBackground( HWND hwnd, int r, int g, int b ) { 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 // delete old background brush
HBRUSH oldBrush = (HBRUSH) ::GetClassLongPtr( hwnd, GCLP_HBRBACKGROUND ); if( fwp->background != NULL )
if( oldBrush != NULL ) ::DeleteObject( fwp->background );
::DeleteObject( oldBrush );
// create new background brush // create new background brush
HBRUSH brush = ::CreateSolidBrush( RGB( r, g, b ) ); fwp->background = ::CreateSolidBrush( RGB( r, g, b ) );
::SetClassLongPtr( hwnd, GCLP_HBRBACKGROUND, (LONG_PTR) brush );
} }
LRESULT CALLBACK FlatWndProc::StaticWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { 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; wParam = wmSizeWParam;
break; break;
case WM_ERASEBKGND:
return WmEraseBkgnd( hwnd, uMsg, wParam, lParam );
case WM_DESTROY: case WM_DESTROY:
return WmDestroy( hwnd, uMsg, wParam, lParam ); return WmDestroy( hwnd, uMsg, wParam, lParam );
} }
return ::CallWindowProc( defaultWndProc, hwnd, uMsg, wParam, lParam ); return ::CallWindowProc( defaultWndProc, hwnd, uMsg, wParam, lParam );
} }
/** /**
* Handle WM_DESTROY * Handle WM_DESTROY
* *
@@ -243,6 +252,8 @@ LRESULT FlatWndProc::WmDestroy( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lPara
// cleanup // cleanup
getEnv()->DeleteGlobalRef( obj ); getEnv()->DeleteGlobalRef( obj );
if( background != NULL )
::DeleteObject( background );
hwndMap->remove( hwnd ); hwndMap->remove( hwnd );
delete this; delete this;
@@ -250,6 +261,23 @@ LRESULT FlatWndProc::WmDestroy( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lPara
return ::CallWindowProc( defaultWndProc2, hwnd, uMsg, wParam, lParam ); 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 * Handle WM_NCCALCSIZE
* *

View File

@@ -42,6 +42,7 @@ private:
HWND hwnd; HWND hwnd;
WNDPROC defaultWndProc; WNDPROC defaultWndProc;
int wmSizeWParam; int wmSizeWParam;
HBRUSH background;
FlatWndProc(); FlatWndProc();
static void initIDs( JNIEnv *env, jobject obj ); static void initIDs( JNIEnv *env, jobject obj );
@@ -49,6 +50,7 @@ private:
static LRESULT CALLBACK StaticWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); static LRESULT CALLBACK StaticWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
LRESULT CALLBACK WindowProc( 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 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 WmNcCalcSize( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam );
LRESULT WmNcHitTest( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam ); LRESULT WmNcHitTest( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam );