Question

I'm using the following code to draw an image that extends at the non client area of my window. The code works, but the control box (the minimize, maximize and close buttons) stays unresponsive, nothing happens when it's clicked. How to draw at the non client area keeping my control box responsive?

Here an screenshot of my app running on Windows 7: My app

And my code:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    WTA_OPTIONS ops;
    ops.dwFlags = WTNCA_NODRAWCAPTION | WTNCA_NODRAWICON;
    ops.dwMask = WTNCA_NODRAWCAPTION | WTNCA_NODRAWICON;
    SetWindowThemeNonClientAttributes(winId(), ops.dwMask, ops.dwFlags);

    MARGINS margins = {-1};
    DwmExtendFrameIntoClientArea(winId(), &margins);
}

bool MainWindow::winEvent(MSG *pMessage, long *result)
{
    HWND hWnd = pMessage->hwnd;
    UINT message = pMessage->message;
    WPARAM wParam = pMessage->wParam;
    LPARAM lParam = pMessage->lParam;

    DwmDefWindowProc(hWnd, message, wParam, lParam, NULL);

    if(message == WM_PAINT)
    {
        HDC hDC = GetWindowDC(hWnd);
        PaintCustomCaption(hWnd, hDC);
        DeleteDC(hDC);
        return true;
    }

    if(message == WM_NCCALCSIZE) return true;

    if(message == WM_NCHITTEST) return true;

    return QWidget::winEvent(pMessage, result);
}

QPixmap pixmap;

void MainWindow::PaintCustomCaption(HWND hWnd, HDC hdc)
{
    RECT rcClient;
    GetClientRect(hWnd, &rcClient);

    HDC hdcPaint = CreateCompatibleDC(hdc);

    HDC hdcRes = CreateCompatibleDC(hdc);

    if (pixmap.isNull()) pixmap = QPixmap("png.png");

    HBITMAP hBmpRes = pixmap.toWinHBITMAP(QPixmap::PremultipliedAlpha);

    SIZE szBmpRes;
    BITMAP rBitmap;
    GetObject(hBmpRes, sizeof (BITMAP), &rBitmap);
    szBmpRes.cx = rBitmap.bmWidth;
    szBmpRes.cy = rBitmap.bmHeight;

    HBITMAP hOldBmpRes = (HBITMAP)SelectObject(hdcRes, hBmpRes);

    if (hdcPaint)
    {
        int cx = rcClient.right - rcClient.left;
        int cy = rcClient.bottom - rcClient.top;

        BITMAPINFO dib = {0};
        dib.bmiHeader.biSize            = sizeof(BITMAPINFOHEADER);
        dib.bmiHeader.biWidth           = cx;
        dib.bmiHeader.biHeight          = -cy;
        dib.bmiHeader.biPlanes          = 1;
        dib.bmiHeader.biBitCount        = 32;
        dib.bmiHeader.biCompression     = BI_RGB;

        HBITMAP hbm = CreateDIBSection(hdc, &dib, DIB_RGB_COLORS, NULL, NULL, 0);

        if (hbm)
        {
            HBITMAP hbmOld = (HBITMAP)SelectObject(hdcPaint, hbm);
            BitBlt(hdcPaint, 0, 0, cx, cy, hdcRes, 0, 0, SRCCOPY);
            BitBlt(hdc, 0, 0, cx, cy, hdcPaint, 0, 0, SRCCOPY);
            SelectObject(hdcPaint, hbmOld);
            DeleteObject(hbm);
        }
        DeleteDC(hdcPaint);
    }
    SelectObject(hdcRes, (HBITMAP)hOldBmpRes);
    DeleteObject(hBmpRes);
    DeleteDC(hdcRes);
}
Was it helpful?

Solution

After several tries i wrote this to code to draw an image that extends at the non client area of the Qt window, tested under Windows 7.

Screenshot of the result:

screenshot

And the relevant code:

At the .pro file:

LIBS += -lGdi32
LIBS += -lUser32
LIBS += -lDwmApi
LIBS += -lUxTheme

At the header file:

#include <dwmapi.h>

#define LEFTEXTENDWIDTH 8
#define RIGHTEXTENDWIDTH 8
#define BOTTOMEXTENDWIDTH 20
#define TOPEXTENDWIDTH 161

private slots:
    void PaintCustomCaption(HWND hWnd, HDC hdc);
    LRESULT HitTestNCA(HWND hWnd, LPARAM lParam);

protected:
    bool winEvent(MSG *pMessage, long *result);

private:
    QPixmap pixmap;

Finally at the cpp file:

bool MainWindow::winEvent(MSG *pMessage, long *result)
{
    HWND hWnd = pMessage->hwnd;
    UINT message = pMessage->message;
    WPARAM wParam = pMessage->wParam;
    LPARAM lParam = pMessage->lParam;

    bool retvalue = false;
    LRESULT lRet = 0;

    DwmDefWindowProc(hWnd, message, wParam, lParam, &lRet);

    if (message == WM_ACTIVATE)
    {
        MARGINS margins;

        margins.cxLeftWidth = LEFTEXTENDWIDTH;
        margins.cxRightWidth = RIGHTEXTENDWIDTH;
        margins.cyBottomHeight = BOTTOMEXTENDWIDTH;
        margins.cyTopHeight = TOPEXTENDWIDTH;

        WTA_OPTIONS ops;
        ops.dwFlags = WTNCA_NODRAWCAPTION | WTNCA_NODRAWICON;
        ops.dwMask = WTNCA_NODRAWCAPTION | WTNCA_NODRAWICON;
        SetWindowThemeNonClientAttributes(hWnd, ops.dwMask, ops.dwFlags);

        DwmExtendFrameIntoClientArea(hWnd, &margins);

        lRet = 0;
        retvalue = false;
    }

    if(message == WM_PAINT)
    {
        HDC hDC = GetWindowDC(hWnd);
        PaintCustomCaption(hWnd, hDC);
        DeleteDC(hDC);
        lRet = 0;
        retvalue = true;
    }

    if(message == WM_NCCALCSIZE)
    {
        lRet = 0;
        retvalue = true;
    }

    if(message == WM_NCHITTEST)
    {
        lRet = HitTestNCA(hWnd, lParam);
        DwmDefWindowProc(hWnd, message, wParam, lParam, &lRet);
        retvalue = true;
    }

    *result = lRet;
    if(retvalue) return true;

    return QWidget::winEvent(pMessage, result);
}

void MainWindow::PaintCustomCaption(HWND hWnd, HDC hdc)
{
    RECT rcClient;
    GetClientRect(hWnd, &rcClient);

    HDC hdcPaint = CreateCompatibleDC(hdc);

    HDC hdcRes = CreateCompatibleDC(hdc);

    if (pixmap.isNull()) pixmap = QPixmap("png.png");

    HBITMAP hBmpRes = pixmap.toWinHBITMAP(QPixmap::PremultipliedAlpha);

    SIZE szBmpRes;
    BITMAP rBitmap;
    GetObject(hBmpRes, sizeof (BITMAP), &rBitmap);
    szBmpRes.cx = rBitmap.bmWidth;
    szBmpRes.cy = rBitmap.bmHeight;

    HBITMAP hOldBmpRes = (HBITMAP)SelectObject(hdcRes, hBmpRes);

    if (hdcPaint)
    {
        int cx = rcClient.right - rcClient.left;
        int cy = rcClient.bottom - rcClient.top;

        BITMAPINFO dib = {0};
        dib.bmiHeader.biSize            = sizeof(BITMAPINFOHEADER);
        dib.bmiHeader.biWidth           = cx;
        dib.bmiHeader.biHeight          = -cy;
        dib.bmiHeader.biPlanes          = 1;
        dib.bmiHeader.biBitCount        = 32;
        dib.bmiHeader.biCompression     = BI_RGB;

        HBITMAP hbm = CreateDIBSection(hdc, &dib, DIB_RGB_COLORS, NULL, NULL, 0);

        if (hbm)
        {
            HBITMAP hbmOld = (HBITMAP)SelectObject(hdcPaint, hbm);
            BitBlt(hdcPaint, 0, 0, cx, cy, hdcRes, 0, 0, SRCCOPY);
            BitBlt(hdc, 0, 0, cx, cy, hdcPaint, 0, 0, SRCCOPY);
            SelectObject(hdcPaint, hbmOld);
            DeleteObject(hbm);
        }
        DeleteDC(hdcPaint);
    }
    SelectObject(hdcRes, (HBITMAP)hOldBmpRes);
    DeleteObject(hBmpRes);
    DeleteDC(hdcRes);
}

LRESULT MainWindow::HitTestNCA(HWND hWnd, LPARAM lParam)
{
    POINT ptMouse = {(int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam)};

    RECT rcWindow;
    GetWindowRect(hWnd, &rcWindow);

    RECT rcFrame = { 0 };
    AdjustWindowRectEx(&rcFrame, WS_OVERLAPPEDWINDOW & ~WS_CAPTION, FALSE, NULL);

    USHORT uRow = 1;
    USHORT uCol = 1;
    bool fOnResizeBorder = false;

    if (ptMouse.y >= rcWindow.top && ptMouse.y < rcWindow.top + TOPEXTENDWIDTH)
    {
        fOnResizeBorder = (ptMouse.y < (rcWindow.top - rcFrame.top));
        uRow = 0;
    }
    else if (ptMouse.y < rcWindow.bottom && ptMouse.y >= rcWindow.bottom - BOTTOMEXTENDWIDTH)
    {
        uRow = 2;
    }

    if (ptMouse.x >= rcWindow.left && ptMouse.x < rcWindow.left + LEFTEXTENDWIDTH)
    {
        uCol = 0;
    }
    else if (ptMouse.x < rcWindow.right && ptMouse.x >= rcWindow.right - RIGHTEXTENDWIDTH)
    {
        uCol = 2;
    }

    LRESULT hitTests[3][3] =
    {
        { HTTOPLEFT,    fOnResizeBorder ? HTTOP : HTCAPTION,    HTTOPRIGHT },
        { HTLEFT,       HTNOWHERE,     HTRIGHT },
        { HTBOTTOMLEFT, HTBOTTOM, HTBOTTOMRIGHT },
    };

    return hitTests[uRow][uCol];
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top