Using Blur Behind on Windows with Qt 5
-
It looks like this functionality will be part of the new module called "Qt Windows Extras":http://doc-snapshot.qt-project.org/qt5-stable/qtwinextras-index.html that will be introduced in "Qt 5.2":http://blog.qt.digia.com/blog/2013/09/30/qt-5-2-alpha-available/.
-
The glass functionality is provided by DwmExtendFrameIntoClientArea() (MSDN link: http://msdn.microsoft.com/en-us/library/windows/desktop/aa969512(v=vs.85).aspx )which is available via a shared system DLL, dwmapi.dll, or by statically linking dwmapi.lib.
Have a look at this post of mine:
http://qt-project.org/forums/viewthread/32286/
The code there uses the DLL method and it's properly working on 32-bit, while on 64-bit it crashes the application. I still have no clue why this happens. The statically linked version instead works fine on both 32 and 64 bit versions. -
Thanks "T3STY":http://qt-project.org/member/139603. I have tested my program (32-bit) on Windows 7 32-bit, Windows 7 64-bit and Windows 8 64-bit and it seems to work on all platforms.
Below you can find my modified versions of the original code posted by "Jens Bache-Wiig":http://blog.qt.digia.com/blog/author/jbache/.
qtwin.h:
@
/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Use, modification and distribution is allowed without limitation,
** warranty, liability or support of any kind.
**
****************************************************************************/#ifndef QTWIN_H
#define QTWIN_H#include "SaoQt.h"
#include <QtGui/QColor>
#include <QtWidgets/QWidget>/*
- This is a helper class for using the Desktop Window Manager
- functionality on Windows 7 and Windows Vista. On other platforms
- these functions will simply not do anything.
*/
class WindowNotifier;
class SAOQT_EXPORT QtWin
{
public:
static bool enableBlurBehindWindow(QWidget* widget, bool enable = true);
static bool extendFrameIntoClientArea(QWidget* widget, int left = -1, int top = -1, int right = -1, int bottom = -1);
static bool isCompositionEnabled();
static QColor colorizationColor();
private:
static WindowNotifier* windowNotifier();
};#endif // QTWIN_H
@ -
qtwin.cpp:
@
/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Use, modification and distribution is allowed without limitation,
** warranty, liability or support of any kind.
**
****************************************************************************/#include "qtwin.h"
#include <QtCore/QLibrary>
#include <QtCore/QList>
#include <QtCore/QPointer>
#include <QtWidgets/QApplication>
#include <QtWidgets/QWidget>#ifdef Q_OS_WIN
#include <QtCore/qt_windows.h>
// Blur behind data structures
#define DWM_BB_ENABLE 0x00000001 // fEnable has been specified
#define DWM_BB_BLURREGION 0x00000002 // hRgnBlur has been specified
#define DWM_BB_TRANSITIONONMAXIMIZED 0x00000004 // fTransitionOnMaximized has been specified
#define WM_DWMCOMPOSITIONCHANGED 0x031E // Composition changed window messagetypedef struct _DWM_BLURBEHIND
{
DWORD dwFlags;
BOOL fEnable;
HRGN hRgnBlur;
BOOL fTransitionOnMaximized;
} DWM_BLURBEHIND, *PDWM_BLURBEHIND;typedef struct _MARGINS
{
int cxLeftWidth;
int cxRightWidth;
int cyTopHeight;
int cyBottomHeight;
} MARGINS, *PMARGINS;typedef HRESULT (WINAPI* PtrDwmIsCompositionEnabled)(BOOL* pfEnabled);
typedef HRESULT (WINAPI* PtrDwmExtendFrameIntoClientArea)(HWND hWnd, const MARGINS* pMarInset);
typedef HRESULT (WINAPI* PtrDwmEnableBlurBehindWindow)(HWND hWnd, const DWM_BLURBEHIND* pBlurBehind);
typedef HRESULT (WINAPI* PtrDwmGetColorizationColor)(DWORD* pcrColorization, BOOL* pfOpaqueBlend);static PtrDwmIsCompositionEnabled pDwmIsCompositionEnabled = 0;
static PtrDwmEnableBlurBehindWindow pDwmEnableBlurBehindWindow = 0;
static PtrDwmExtendFrameIntoClientArea pDwmExtendFrameIntoClientArea = 0;
static PtrDwmGetColorizationColor pDwmGetColorizationColor = 0;/*
- Internal helper class that notifies windows if the
- DWM compositing state changes and updates the widget
- flags correspondingly.
/
class WindowNotifier : public QWidget
{
public:
WindowNotifier() { winId(); }
void addWidget(QWidget widget) { widgets.append(widget); }
void removeWidget(QWidget* widget) { widgets.removeAll(widget); }
bool winEvent(MSG* message, long* result);
private:
QWidgetList widgets;
};
static bool resolveLibs()
{
if (!pDwmIsCompositionEnabled)
{
QLibrary dwmLib(QStringLiteral("dwmapi"));
pDwmIsCompositionEnabled = (PtrDwmIsCompositionEnabled)dwmLib.resolve("DwmIsCompositionEnabled");
pDwmExtendFrameIntoClientArea = (PtrDwmExtendFrameIntoClientArea)dwmLib.resolve("DwmExtendFrameIntoClientArea");
pDwmEnableBlurBehindWindow = (PtrDwmEnableBlurBehindWindow)dwmLib.resolve("DwmEnableBlurBehindWindow");
pDwmGetColorizationColor = (PtrDwmGetColorizationColor)dwmLib.resolve("DwmGetColorizationColor");
}
return pDwmIsCompositionEnabled != 0;
}#endif // Q_OS_WIN
/*!
- Checks and returns true if Windows DWM composition
- is currently enabled on the system.
*/
bool QtWin::isCompositionEnabled()
{
#ifdef Q_OS_WIN
if (resolveLibs())
{
BOOL isEnabled = false;
HRESULT hr = pDwmIsCompositionEnabled(&isEnabled);
if (SUCCEEDED(hr))
return isEnabled;
}
#endif // Q_OS_WIN
return false;
}/*!
- Enables Blur behind on a Widget.
- \a enable tells if the blur should be enabled or not
/
bool QtWin::enableBlurBehindWindow(QWidget widget, bool enable)
{
Q_ASSERT(widget);
bool result = false;
#ifdef Q_OS_WIN
if (resolveLibs())
{
DWM_BLURBEHIND bb = {0};
bb.fEnable = enable;
bb.dwFlags = DWM_BB_ENABLE;
bb.hRgnBlur = NULL;
widget->setAttribute(Qt::WA_TranslucentBackground, enable);
widget->setAttribute(Qt::WA_NoSystemBackground, enable);
HRESULT hr = pDwmEnableBlurBehindWindow(reinterpret_cast<HWND>(widget->winId()), &bb);
if (SUCCEEDED(hr))
{
result = true;
windowNotifier()->addWidget(widget);
}
}
#endif // Q_OS_WINreturn result;
}/*!
- ExtendFrameIntoClientArea.
- This controls the rendering of the frame inside the window.
- Note that passing margins of -1 (the default value) will completely
- remove the frame from the window.
- \note You should not call enableBlurBehindWindow before calling
-
this functions
/
bool QtWin::extendFrameIntoClientArea(QWidget widget, int left, int top, int right, int bottom)
{
Q_ASSERT(widget);
Q_UNUSED(left);
Q_UNUSED(top);
Q_UNUSED(right);
Q_UNUSED(bottom);bool result = false;
#ifdef Q_OS_WIN
if (resolveLibs())
{
MARGINS m = {left, top, right, bottom};
HRESULT hr = pDwmExtendFrameIntoClientArea(reinterpret_cast<HWND>(widget->winId()), &m);
if (SUCCEEDED(hr))
{
result = true;
windowNotifier()->addWidget(widget);
}
widget->setAttribute(Qt::WA_TranslucentBackground, result);
}
#endif // Q_OS_WINreturn result;
}/*!
- Returns the current colorizationColor for the window.
*/
QColor QtWin::colorizationColor()
{
QColor resultColor = QApplication::palette().window().color();
#ifdef Q_OS_WIN
if (resolveLibs())
{
DWORD color = 0;
BOOL opaque = FALSE;
HRESULT hr = pDwmGetColorizationColor(&color, &opaque);
if (SUCCEEDED(hr))
resultColor = QColor(color);
}
#endif // Q_OS_WINreturn resultColor;
}#ifdef Q_OS_WIN
WindowNotifier* QtWin::windowNotifier()
{
static WindowNotifier* windowNotifierInstance = 0;
if (!windowNotifierInstance)
windowNotifierInstance = new WindowNotifier();
return windowNotifierInstance;
}/* Notify all enabled windows that the DWM state changed */
bool WindowNotifier::winEvent(MSG *message, long result)
{
if (message && message->message == WM_DWMCOMPOSITIONCHANGED)
{
bool compositionEnabled = QtWin::isCompositionEnabled();
foreach(QWidget widget, widgets)
{
if (widget)
widget->setAttribute(Qt::WA_NoSystemBackground, compositionEnabled);
widget->update();
}
}
return QWidget::nativeEvent("windows_generic_MSG", message, result);
}#endif // Q_OS_WIN
@ -
SaoQt.h:
@
#ifndef SAOQT_SAOQT_H
#define SAOQT_SAOQT_H#include <QtCore/QtGlobal>
#if defined(SAOQT_LIB)
define SAOQT_EXPORT Q_DECL_EXPORT
#else
define SAOQT_EXPORT Q_DECL_IMPORT
#endif
#endif // SAOQT_SAOQT_H
@ -
I was just testing this code in an app today and I got a few issues.
First is in qtwin.h when the class QtWin is declared with SAOQT_EXPORT:
http://img.networkdigitale.com/di/EVMM/SAOQT_EXPORT.png
If I remove SAOQT_EXPORT from QtWin's declaration the code compiles just fine.
Second is glitches:
http://img.networkdigitale.com/di/JKDP/glitches.png
In this image I have a QWidget parent, and 10 QSlider + QLabel childs. As soon as I move the sliders I get those glitches you see in the image. They go away as soon as I resize the window, probably because resizing invokes a window redraw.How do I fix those issues?
-
SAOQT_EXPORT handles the magic needed when building a dynamic link library and is defined in SaoQt.h (my library is called SaoQt, that's why). When building the library SAOQT_LIB is defined and when using the library in an application SAOQT_LIB is not defined.
The original code worked without problems using Qt 4.8. However, after moving to Qt 5 I have update problems for a transparent menu button. I haven't really investigated the problem so I can't help you. After I read about the "Qt Windows Extras":http://doc-snapshot.qt-project.org/qt5-stable/qtwinextras-index.html that will be introduced in "Qt 5.2":http://blog.qt.digia.com/blog/2013/11/29/qt-5-2-release-candidate-1-available/ I decided to wait for Qt 5.2 before spending more time on this.
-
I have now switched to the Qt Windows Extras found in Qt 5.2 and my application works without any update problems (glitches). To avoid the glitches I had to use:
@
setStyleSheet("QMainWindow { background: transparent; }");
@Without it, I experienced the same glitches as I had with the old code.
I have also tested the old code with the same call to styleSheet but the old code still had the glitches.
-
Can you also post the original source code of the Qt4 version please?
Thank you in advance!
-
My latest version for Qt 4.x (compiled with Qt 4.8.1):
qtwin.h:
@
/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Use, modification and distribution is allowed without limitation,
** warranty, liability or support of any kind.
**
****************************************************************************/#ifndef QTWIN_H
#define QTWIN_H#include "SaoQt.h"
#include <QColor>
#include <QWidget>/*
- This is a helper class for using the Desktop Window Manager
- functionality on Windows 7 and Windows Vista. On other platforms
- these functions will simply not do anything.
*/
class WindowNotifier;
class SAOQT_EXPORT QtWin
{
public:
static bool enableBlurBehindWindow(QWidget* widget, bool enable = true);
static bool extendFrameIntoClientArea(QWidget* widget, int left = -1, int top = -1, int right = -1, int bottom = -1);
static bool isCompositionEnabled();
static QColor colorizationColor();
private:
static WindowNotifier* windowNotifier();
};#endif // QTWIN_H
@ -
qtwin.cpp:
@
/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Use, modification and distribution is allowed without limitation,
** warranty, liability or support of any kind.
**
****************************************************************************/#include "qtwin.h"
#include <QLibrary>
#include <QApplication>
#include <QWidget>
#include <QList>
#include <QPointer>#ifdef Q_WS_WIN
#include <qt_windows.h>
// Blur behind data structures
#define DWM_BB_ENABLE 0x00000001 // fEnable has been specified
#define DWM_BB_BLURREGION 0x00000002 // hRgnBlur has been specified
#define DWM_BB_TRANSITIONONMAXIMIZED 0x00000004 // fTransitionOnMaximized has been specified
#define WM_DWMCOMPOSITIONCHANGED 0x031E // Composition changed window messagetypedef struct _DWM_BLURBEHIND
{
DWORD dwFlags;
BOOL fEnable;
HRGN hRgnBlur;
BOOL fTransitionOnMaximized;
} DWM_BLURBEHIND, *PDWM_BLURBEHIND;typedef struct _MARGINS
{
int cxLeftWidth;
int cxRightWidth;
int cyTopHeight;
int cyBottomHeight;
} MARGINS, *PMARGINS;typedef HRESULT (WINAPI* PtrDwmIsCompositionEnabled)(BOOL* pfEnabled);
typedef HRESULT (WINAPI* PtrDwmExtendFrameIntoClientArea)(HWND hWnd, const MARGINS* pMarInset);
typedef HRESULT (WINAPI* PtrDwmEnableBlurBehindWindow)(HWND hWnd, const DWM_BLURBEHIND* pBlurBehind);
typedef HRESULT (WINAPI* PtrDwmGetColorizationColor)(DWORD* pcrColorization, BOOL* pfOpaqueBlend);static PtrDwmIsCompositionEnabled pDwmIsCompositionEnabled = 0;
static PtrDwmEnableBlurBehindWindow pDwmEnableBlurBehindWindow = 0;
static PtrDwmExtendFrameIntoClientArea pDwmExtendFrameIntoClientArea = 0;
static PtrDwmGetColorizationColor pDwmGetColorizationColor = 0;/*
- Internal helper class that notifies windows if the
- DWM compositing state changes and updates the widget
- flags correspondingly.
/
class WindowNotifier : public QWidget
{
public:
WindowNotifier() { winId(); }
void addWidget(QWidget widget) { widgets.append(widget); }
void removeWidget(QWidget* widget) { widgets.removeAll(widget); }
bool winEvent(MSG* message, long* result);
private:
QWidgetList widgets;
};
static bool resolveLibs()
{
if (!pDwmIsCompositionEnabled)
{
QLibrary dwmLib(QString::fromAscii("dwmapi"));
pDwmIsCompositionEnabled = (PtrDwmIsCompositionEnabled)dwmLib.resolve("DwmIsCompositionEnabled");
pDwmExtendFrameIntoClientArea = (PtrDwmExtendFrameIntoClientArea)dwmLib.resolve("DwmExtendFrameIntoClientArea");
pDwmEnableBlurBehindWindow = (PtrDwmEnableBlurBehindWindow)dwmLib.resolve("DwmEnableBlurBehindWindow");
pDwmGetColorizationColor = (PtrDwmGetColorizationColor)dwmLib.resolve("DwmGetColorizationColor");
}
return pDwmIsCompositionEnabled != 0;
}#endif // Q_WS_WIN
/*!
- Checks and returns true if Windows DWM composition
- is currently enabled on the system.
*/
bool QtWin::isCompositionEnabled()
{
#ifdef Q_WS_WIN
if (resolveLibs())
{
BOOL isEnabled = false;
HRESULT hr = pDwmIsCompositionEnabled(&isEnabled);
if (SUCCEEDED(hr))
return isEnabled;
}
#endif // Q_WS_WIN
return false;
}/*!
- Enables Blur behind on a Widget.
- \a enable tells if the blur should be enabled or not
/
bool QtWin::enableBlurBehindWindow(QWidget widget, bool enable)
{
Q_ASSERT(widget);
bool result = false;
#ifdef Q_WS_WIN
if (resolveLibs())
{
DWM_BLURBEHIND bb = {0};
bb.fEnable = enable;
bb.dwFlags = DWM_BB_ENABLE;
bb.hRgnBlur = NULL;
widget->setAttribute(Qt::WA_TranslucentBackground, enable);
widget->setAttribute(Qt::WA_NoSystemBackground, enable);
HRESULT hr = pDwmEnableBlurBehindWindow(widget->winId(), &bb);
if (SUCCEEDED(hr))
{
result = true;
windowNotifier()->addWidget(widget);
}
}
#endif // Q_WS_WINreturn result;
}/*!
- ExtendFrameIntoClientArea.
- This controls the rendering of the frame inside the window.
- Note that passing margins of -1 (the default value) will completely
- remove the frame from the window.
- \note You should not call enableBlurBehindWindow before calling
-
this functions
/
bool QtWin::extendFrameIntoClientArea(QWidget widget, int left, int top, int right, int bottom)
{
Q_ASSERT(widget);
Q_UNUSED(left);
Q_UNUSED(top);
Q_UNUSED(right);
Q_UNUSED(bottom);bool result = false;
#ifdef Q_WS_WIN
if (resolveLibs())
{
MARGINS m = {left, top, right, bottom};
HRESULT hr = pDwmExtendFrameIntoClientArea(widget->winId(), &m);
if (SUCCEEDED(hr))
{
result = true;
windowNotifier()->addWidget(widget);
}
widget->setAttribute(Qt::WA_TranslucentBackground, result);
}
#endif // Q_WS_WINreturn result;
}/*!
- Returns the current colorizationColor for the window.
*/
QColor QtWin::colorizationColor()
{
QColor resultColor = QApplication::palette().window().color();
#ifdef Q_WS_WIN
if (resolveLibs())
{
DWORD color = 0;
BOOL opaque = FALSE;
HRESULT hr = pDwmGetColorizationColor(&color, &opaque);
if (SUCCEEDED(hr))
resultColor = QColor(color);
}
#endif // Q_WS_WINreturn resultColor;
}#ifdef Q_WS_WIN
WindowNotifier* QtWin::windowNotifier()
{
static WindowNotifier* windowNotifierInstance = 0;
if (!windowNotifierInstance)
windowNotifierInstance = new WindowNotifier();
return windowNotifierInstance;
}/* Notify all enabled windows that the DWM state changed */
bool WindowNotifier::winEvent(MSG *message, long result)
{
if (message && message->message == WM_DWMCOMPOSITIONCHANGED)
{
bool compositionEnabled = QtWin::isCompositionEnabled();
foreach(QWidget widget, widgets)
{
if (widget)
widget->setAttribute(Qt::WA_NoSystemBackground, compositionEnabled);
widget->update();
}
}
return QWidget::winEvent(message, result);
}#endif // Q_WS_WIN
@ -
SaoQt.h is identical to the version given above.