示例#1
0
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;
}