LRESULT CustomCaptionFrameProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, bool *callDef, WindowInfo *win) { if (dwm::IsCompositionEnabled()) { // Pass the messages to DwmDefWindowProc first. It serves the hit testing for the buttons. LRESULT res; if (TRUE == dwm::DefWindowProc_(hwnd, msg, wParam, lParam, &res)) { *callDef = false; return res; } switch (msg) { case WM_ERASEBKGND: { // Erase the background only under the extended frame. *callDef = false; if (win->extendedFrameHeight == 0) return TRUE; ClientRect rc(hwnd); rc.dy = win->extendedFrameHeight; HRGN extendedFrameRegion = CreateRectRgn(rc.x, rc.y, rc.x + rc.dx, rc.y + rc.dy); int newRegionComplexity = ExtSelectClipRgn((HDC)wParam, extendedFrameRegion, RGN_AND); DeleteObject(extendedFrameRegion); if (newRegionComplexity == NULLREGION) return TRUE; } return DefWindowProc(hwnd, msg, wParam, lParam); case WM_SIZE: // Extend the translucent frame in the client area. if (wParam == SIZE_MAXIMIZED || wParam == SIZE_RESTORED) { long ws = GetWindowLong(hwnd, GWL_STYLE); int frameThickness = !(ws & WS_THICKFRAME) ? 0 : GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER); int captionHeight = !(ws & WS_CAPTION) ? 0 : GetTabbarHeight(win, IsZoomed(hwnd) ? 1.f : CAPTION_TABBAR_HEIGHT_FACTOR); MARGINS margins = {0, 0, frameThickness + captionHeight, 0}; dwm::ExtendFrameIntoClientArea(hwnd, &margins); win->extendedFrameHeight = frameThickness + captionHeight; } break; case WM_NCACTIVATE: win->caption->UpdateColors((bool)wParam); if (!IsIconic(hwnd)) RedrawWindow(win->hwndCaption, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); break; } } else { switch (msg) { case WM_SETTINGCHANGE: if (wParam == SPI_SETNONCLIENTMETRICS) RelayoutCaption(win); break; case WM_NCPAINT: DrawFrame(hwnd, win); *callDef = false; return 0; case WM_NCACTIVATE: win->caption->UpdateColors((bool)wParam); for (int i = CB_BTN_FIRST; i < CB_BTN_COUNT; i++) win->caption->btn[i].inactive = wParam == FALSE; if (!IsIconic(hwnd)) { DrawFrame(hwnd, win); RedrawWindow(win->hwndCaption, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); *callDef = false; return TRUE; } break; case WM_NCUAHDRAWCAPTION: case WM_NCUAHDRAWFRAME: DrawFrame(hwnd, win); *callDef = false; return TRUE; case WM_POPUPSYSTEMMENU: case WM_SETCURSOR: case WM_SETTEXT: case WM_SETICON: if (!win->caption->theme) { // Remove the WS_VISIBLE style to prevent DefWindowProc from drawing // in the caption's area when processing these mesages. ToggleWindowStyle(hwnd, WS_VISIBLE, false); LRESULT res = DefWindowProc(hwnd, msg, wParam, lParam); ToggleWindowStyle(hwnd, WS_VISIBLE, true); *callDef = false; return res; } break; } } // These messages must be handled in both modes - with or without DWM composition. switch (msg) { case WM_NCCALCSIZE: { // In order to have custom caption, we have to include its area in the client rectangle. RECT *r = wParam == TRUE ? &((LPNCCALCSIZE_PARAMS)lParam)->rgrc[0] : (RECT *)lParam; RECT rWindow = *r; // Let DefWindowProc calculate the client rectangle. DefWindowProc(hwnd, msg, wParam, lParam); RECT rClient = *r; // Modify the client rectangle to include the caption's area. if (dwm::IsCompositionEnabled()) rClient.top = rWindow.top; else rClient.top = rWindow.top + rWindow.bottom - rClient.bottom; rClient.bottom--; // prevents the hiding of the topmost windows, when this window is maximized *r = rClient; *callDef = false; } return 0; case WM_NCHITTEST: { // Provide hit testing for the caption. PointI pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); RectI rClient = MapRectToWindow(ClientRect(hwnd), hwnd, HWND_DESKTOP); WindowRect rCaption(win->hwndCaption); if (rClient.Contains(pt) && pt.y < rCaption.y + rCaption.dy) { *callDef = false; if (pt.y < rCaption.y) return HTTOP; return HTCAPTION; } } break; case WM_NCRBUTTONUP: // Prepare and show the system menu. if (wParam == HTCAPTION) { HMENU menu = GetUpdatedSystemMenu(hwnd); UINT flags = TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD; if (GetSystemMetrics(SM_MENUDROPALIGNMENT)) flags |= TPM_RIGHTALIGN; WPARAM cmd = TrackPopupMenu(menu, flags, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0, hwnd, NULL); if (cmd) PostMessage(hwnd, WM_SYSCOMMAND, cmd, 0); *callDef = false; return 0; } break; case WM_SYSCOMMAND: if (wParam == SC_KEYMENU) { // Show the "menu bar" (and the desired submenu) gMenuAccelPressed = (WCHAR)lParam; if (' ' == gMenuAccelPressed) { // map space to the accelerator of the Window menu if (str::FindChar(_TR("&Window"), '&')) gMenuAccelPressed = *(str::FindChar(_TR("&Window"), '&') + 1); } PostMessage(win->hwndCaption, WM_COMMAND, MAKELONG(BTN_ID_FIRST + CB_MENU, BN_CLICKED), 0); *callDef = false; return 0; } break; case WM_INITMENUPOPUP: if (gMenuAccelPressed) { // poorly documented hack: find the menu window and send it the accelerator key HWND hMenu = FindWindow(UNDOCUMENTED_MENU_CLASS_NAME, NULL); if (hMenu) PostMessage(hMenu, WM_CHAR, gMenuAccelPressed, 0); gMenuAccelPressed = 0; } break; case WM_SYSCOLORCHANGE: win->caption->UpdateColors(hwnd == GetForegroundWindow()); break; case WM_DWMCOMPOSITIONCHANGED: win->caption->UpdateBackgroundAlpha(); ClientRect cr(hwnd); SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE); if (ClientRect(hwnd) == cr) SendMessage(hwnd, WM_SIZE, 0, MAKELONG(cr.dx, cr.dy)); *callDef = false; return 0; } *callDef = true; return 0; }