I want to create a Windows API app in C which renders the menu and the caption buttons in the same non-client area, similar to Firefox
In order to do this, I've determined the solution needs to:
The solution needs to work on Windows 7, 8, and 10 (and ideally future versions too).
I have a test program available on GitHub.
In my app, I've overridden the appropriate events:
WM_NCCALCSIZE
, WM_NCHITTEST
, WM_NCLBUTTONDOWN
, WM_NCLBUTTONUP
, WM_NCMOUSEMOVE
, WM_NCPAINT
And then I repaint non-client areas on these events:
WM_NCACTIVATE
, WM_SETTEXT
Here's an example of how I'm doing the rendering:
// globals set elsewhere
RECT customAreaRect, minRect, maxRect, closeRect, coverMenuRect;
BOOL maximized;
// ...
LRESULT OnPaintNCA(HWND hWnd, WPARAM wParam, LPARAM lParam) {
RECT windowRect;
HRGN hRgn = NULL;
GetWindowRect(hWnd, &windowRect);
if (wParam == 1) {
hRgn = CreateRectRgnIndirect(&windowRect);
} else {
hRgn = (HRGN)wParam;
}
if (hRgn) {
// Carve out the area for custom content
HRGN captionButtonRgn = CreateRectRgnIndirect(&customAreaRect);
CombineRgn(hRgn, hRgn, captionButtonRgn, RGN_XOR);
DeleteObject(captionButtonRgn);
// Force default painting for non-client area
LRESULT ret = DefWindowProc(hWnd, WM_NCPAINT, (WPARAM)hRgn, 0);
// black background covering part of menu, behind buttons
HDC hDC = GetWindowDC(hWnd);
FillRect(hDC, &coverMenuRect, (HBRUSH)GetStockObject(BLACK_BRUSH));
HTHEME hTheme = OpenThemeData(hWnd, TEXT("WINDOW"));
DrawThemeBackground(hTheme, hDC, WP_MINBUTTON, partState, minRect, NULL);
DrawThemeBackground(hTheme, hDC, maximized ? WP_RESTOREBUTTON : WP_MAXBUTTON, partState, maxRect, NULL);
DrawThemeBackground(hTheme, hDC, WP_CLOSEBUTTON, partState, closeRect, NULL);
CloseThemeData(hTheme);
}
}
The rendered result looks like this: Unfortunately, the styles used for the parts (minimize, maximize/restore, close) look like the styles for Windows 7/8, and not the native Windows 10 controls. I've been searching for a way to do this for several days without luck. I need help understanding how to render these buttons for Windows 10 using the Windows API.
My first hunch was that I need to properly enable Visual Styles.
Per this article, calls checking the OS version will get Windows 8 unless you specifically target Windows 10 via a manifest. Click here to view my manifest. This does work:
Per the official "Enabling Visual Styles" article, I made sure to use Common Controls version 6.
Here are some screenshots of relevant project settings that I have tried:
I'm running low on things to try. Before throwing in the towel, there were some things I was going to try:
Idea 1: using GetThemeStream which allows you to retrieve the size/bitmap for controls.
HMODULE themeFile = LoadLibraryEx(TEXT("C:\\Windows\\Resources\\Themes\\aero\\aero.msstyles"), NULL, LOAD_LIBRARY_AS_DATAFILE);
GetThemeStream(h, WP_MAXBUTTON, MAXBS_NORMAL, TMT_DISKSTREAM, (void**)&buffer, &bufferSize, themeFile);
Idea 2: copy the non-client area from a hidden window which has the caption area (and minimize, maximize, close buttons).
I think the issue comes from trying to use WM_NCPAINT
on OS version >= Win Vista.
Since Vista all NC rendering is controlled by DWM (desktop window manager). If you still dare to handle WM_NCPAINT
, DWM rendering will be turned off and you get "old-school" look:
From the Shell Revealed Blog:
The DWM doesnt have any legacy worries because applications cannot draw inside the glass frame, since its rendered and managed by a totally different process. If an application tries to do it, Windows will detect it and remove the glass frame entirely (and therefore revert to the Basic frame), so that the application can draw what it wants to draw.
To get proper results, you have to do it the "DWM way" (specifically the sections "Removing the Standard Frame" and "Drawing in the Extended Frame Window"). This works by letting DWM render the frame within the client area, so you can paint over it. Also with this solution you don't have to draw the caption buttons on your own. This answer summarizes the required steps (under "Aero supported solution").
The caveat is that you propably have to draw the menu on your own and you can't use most of GDI API for that, because GDI ignores the alpha channel and things will look ugly if the frame is translucent (Vista and Win 7 by default, Win8+ with extensions). BitBlt()
works if the source is a memory DC that contains a 32bpp bitmap with an alpha channel. GDI+ works aswell.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With