Search code examples
c++user-interfacewinapiblurqt6

How to add Win32 API (Blur) in Qt6 QWidget?


I am just beginner and I try to make a simple Qt6 widget with blurring behind for Windows 10-11 OS. I know that Qt6 doesn't provide this from the box and I need to use the Windows API for that.

Unfortunately, I don't know the Windows API, but I found a working code with the effect I needed:

Win32 API:

#include <windows.h>

TCHAR szClassName[] = TEXT("Window");

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        const HDC hdc = BeginPaint(hWnd, &ps);
        SetTextColor(hdc, RGB(255, 255, 255));
        SetBkMode(hdc, TRANSPARENT);
        TextOut(hdc, 10, 10, TEXT("Hello, world!"), 13);
        EndPaint(hWnd, &ps);
    }
    break;
    case WM_NCHITTEST:
        wParam = DefWindowProc(hWnd, msg, wParam, lParam);
        if (wParam == HTCLIENT)
            return HTCAPTION;
        else
            return wParam;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, msg, wParam, lParam);
    }
    return 0;
}

void SetWindowBlur(HWND hWnd)
{
    const HINSTANCE hModule = LoadLibrary(TEXT("user32.dll"));
    if (hModule)
    {
        struct ACCENTPOLICY
        {
            int nAccentState;
            int nFlags;
            int nColor;
            int nAnimationId;
        };
        struct WINCOMPATTRDATA
        {
            int nAttribute;
            PVOID pData;
            ULONG ulDataSize;
        };
        typedef BOOL(WINAPI* pSetWindowCompositionAttribute)(HWND, WINCOMPATTRDATA*);
        const pSetWindowCompositionAttribute SetWindowCompositionAttribute = (pSetWindowCompositionAttribute)GetProcAddress(hModule, "SetWindowCompositionAttribute");
        if (SetWindowCompositionAttribute)
        {
            ACCENTPOLICY policy = { 3, 0, 0, 0 };
            WINCOMPATTRDATA data = { 19, &policy, sizeof(ACCENTPOLICY) };
            SetWindowCompositionAttribute(hWnd, &data);
        }
        FreeLibrary(hModule);
    }
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInst, LPSTR pCmdLine, int nCmdShow)
{
    MSG msg;
    WNDCLASS wndclass = {
        0,
        WndProc,
        0,
        0,
        hInstance,
        0,
        LoadCursor(0, IDC_ARROW),
        (HBRUSH)GetStockObject(BLACK_BRUSH),
        0,
        szClassName
    };
    RegisterClass(&wndclass);
    HWND hWnd = CreateWindow(
        szClassName,
        TEXT("Window"),
        WS_POPUPWINDOW,
        0,
        0,
        640,
        480,
        0,
        0,
        hInstance,
        0
    );
    SetWindowBlur(hWnd);
    ShowWindow(hWnd, SW_SHOWDEFAULT);
    UpdateWindow(hWnd);
    while (GetMessage(&msg, 0, 0, 0))
    {
           TranslateMessage(&msg);
           DispatchMessage(&msg);
    }
    return msg.wParam;
}

In Qt6 I have a frameless semi-transparent widget with an overridden painterEvent where all the painting happens. The main question is: how can I draw the blur effect from win32 API in the painterEvent? Just in case, here is a simplified Qt6 code:

#include <QtWidgets/QMainWindow>
#include <QPainter>
#include <qapplication.h>

class Qt6WinAPI_Test : public QWidget
{
    Q_OBJECT

public:
    Qt6WinAPI_Test(QWidget *parent = nullptr)
    {
        setWindowFlag(Qt::FramelessWindowHint);
        setAttribute(Qt::WA_TranslucentBackground);
    }
    ~Qt6WinAPI_Test(){}
protected:
    void paintEvent(QPaintEvent* event) override
    {
        QPainter painter(this);
        painter.fillRect(this->rect(), QColor(255, 255, 255, 150));
        painter.end();
    }
};

Solution

  • Thanks to @IInspectable for the hint, maybe it will be useful for someone:

    #include <QPainter>
    #include <QWidget>
    #include <qapplication.h>
    #include <windows.h>
    
    class Qt6WinAPI_Test : public QWidget
    {
        Q_OBJECT
    
    public:
        Qt6WinAPI_Test(QWidget *parent = nullptr)
        {
            this->resize(640, 480);
            setWindowFlags(Qt::FramelessWindowHint);
            setAttribute(Qt::WA_TranslucentBackground);
        }
        ~Qt6WinAPI_Test(){}
    protected:
        void paintEvent(QPaintEvent* event) override
        {
            QPainter painter(this);
            painter.fillRect(this->rect(), QColor(0, 0, 0, 1));
        }
        bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result) override
        {
            static bool firtsCall{true};
            MSG * msg = reinterpret_cast<MSG*>(message);
            if(firtsCall)
            {
                SetWindowBlur(msg->hwnd);
                firstCall = false;
            }
            return false;
        }
    private: 
    void SetWindowBlur(HWND hWnd)
    {
        const HINSTANCE hModule = LoadLibrary(TEXT("user32.dll"));
        if (hModule)
        {
            struct ACCENTPOLICY
            {
                int nAccentState;
                int nFlags;
                int nColor;
                int nAnimationId;
            };
            struct WINCOMPATTRDATA
            {
                int nAttribute;
                PVOID pData;
                ULONG ulDataSize;
            };
            typedef BOOL(WINAPI* pSetWindowCompositionAttribute)(HWND, WINCOMPATTRDATA*);
            const pSetWindowCompositionAttribute SetWindowCompositionAttribute = (pSetWindowCompositionAttribute)GetProcAddress(hModule, "SetWindowCompositionAttribute");
            if (SetWindowCompositionAttribute)
            {
                ACCENTPOLICY policy = { 3, 0, 0, 0 };
                WINCOMPATTRDATA data = { 19, &policy, sizeof(ACCENTPOLICY) };
                SetWindowCompositionAttribute(hWnd, &data);
            }
            FreeLibrary(hModule);
        }
    }
    };