c++qtwindows-10qmlfluent-design

How to get a blurred translucent QML Window (similar to Fluent Design guidelines) on Windows 10?


I would like to get a semi-transparent blurred window in QML on Windows 10 similar to the Fluent Design guidelines (example). I know you can make a transparent window:

Window{
    visible: true
    color: "transparent"
}

but this doesn't achieve the blur effect I am looking at. I am also aware that one can blur elements inside the windows using QtGraphicalEffects like FastBlur but I would like to blur the entire window itself. Is there a way to achieve this? I have also tried using the QtWinExtras module and call QtWin::enableBlurBehindWindow but this doesn't work either:

    QObject *root = engine.rootObjects()[0];
    QQuickWindow *window = qobject_cast<QQuickWindow *>(root);
    if (!window) {
        qFatal("Error: Your root item has to be a window.");
        return -1;
    }
    QtWin::enableBlurBehindWindow(window);

Solution

  • Ok so I found a solution that turned out to be easier than I thought it would be. Officially Microsoft does not provide an API to achieve what I was looking for. I stumbled across this thread and I found this. From there I adapted the code to my needs, I created a header file containing:

    #ifndef STRUCTS_H
    #define STRUCTS_H
    #include <windef.h>
    #pragma once
    
    typedef enum _WINDOWCOMPOSITIONATTRIB
    {
        WCA_UNDEFINED = 0,
        WCA_NCRENDERING_ENABLED = 1,
        WCA_NCRENDERING_POLICY = 2,
        WCA_TRANSITIONS_FORCEDISABLED = 3,
        WCA_ALLOW_NCPAINT = 4,
        WCA_CAPTION_BUTTON_BOUNDS = 5,
        WCA_NONCLIENT_RTL_LAYOUT = 6,
        WCA_FORCE_ICONIC_REPRESENTATION = 7,
        WCA_EXTENDED_FRAME_BOUNDS = 8,
        WCA_HAS_ICONIC_BITMAP = 9,
        WCA_THEME_ATTRIBUTES = 10,
        WCA_NCRENDERING_EXILED = 11,
        WCA_NCADORNMENTINFO = 12,
        WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,
        WCA_VIDEO_OVERLAY_ACTIVE = 14,
        WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,
        WCA_DISALLOW_PEEK = 16,
        WCA_CLOAK = 17,
        WCA_CLOAKED = 18,
        WCA_ACCENT_POLICY = 19,
        WCA_FREEZE_REPRESENTATION = 20,
        WCA_EVER_UNCLOAKED = 21,
        WCA_VISUAL_OWNER = 22,
        WCA_HOLOGRAPHIC = 23,
        WCA_EXCLUDED_FROM_DDA = 24,
        WCA_PASSIVEUPDATEMODE = 25,
        WCA_LAST = 26
    } WINDOWCOMPOSITIONATTRIB;
    
    typedef struct _WINDOWCOMPOSITIONATTRIBDATA
    {
        WINDOWCOMPOSITIONATTRIB Attrib;
        PVOID pvData;
        SIZE_T cbData;
    } WINDOWCOMPOSITIONATTRIBDATA;
    
    typedef enum _ACCENT_STATE
    {
        ACCENT_DISABLED = 0,
        ACCENT_ENABLE_GRADIENT = 1,
        ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
        ACCENT_ENABLE_BLURBEHIND = 3,
        ACCENT_ENABLE_ACRYLICBLURBEHIND = 4, // RS4 1803
        ACCENT_ENABLE_HOSTBACKDROP = 5, // RS5 1809
        ACCENT_INVALID_STATE = 6
    } ACCENT_STATE;
    
    typedef struct _ACCENT_POLICY
    {
        ACCENT_STATE AccentState;
        DWORD AccentFlags;
        DWORD GradientColor;
        DWORD AnimationId;
    } ACCENT_POLICY;
    
    typedef BOOL (WINAPI *pfnGetWindowCompositionAttribute)(HWND, WINDOWCOMPOSITIONATTRIBDATA*);
    
    typedef BOOL (WINAPI *pfnSetWindowCompositionAttribute)(HWND, WINDOWCOMPOSITIONATTRIBDATA*);
    #endif // STRUCTS_H
    

    And then in my main.cpp:

    #ifdef Q_OS_WIN
    #include <QQuickWindow>
    #include <windows.h>
    #include <WinUser.h>
    #include "structs.h" // my header file
    #endif
    
    #ifdef Q_OS_WIN
        QObject *root = engine.rootObjects()[0];
        QQuickWindow *window = qobject_cast<QQuickWindow *>(root);
        if (!window) {
            qFatal("Error: Your root item has to be a window.");
            return -1;
        }
        HWND hwnd = (HWND)window->winId();
        HMODULE hUser = GetModuleHandle(L"user32.dll");
        if (hUser)
        {
            pfnSetWindowCompositionAttribute setWindowCompositionAttribute = (pfnSetWindowCompositionAttribute)GetProcAddress(hUser, "SetWindowCompositionAttribute");
            if (setWindowCompositionAttribute)
            {
                ACCENT_POLICY accent = { ACCENT_ENABLE_BLURBEHIND, 0, 0, 0 };
                WINDOWCOMPOSITIONATTRIBDATA data;
                data.Attrib = WCA_ACCENT_POLICY;
                data.pvData = &accent;
                data.cbData = sizeof(accent);
                setWindowCompositionAttribute(hwnd, &data);
            }
        }
    #endif
    

    This enables the "Acrylic Material" effect I was looking for (in QML you have to set the window color to "transparent").