Using Blur Behind on Windows with Qt 5
-
Back in September 2009 "Jens Bache-Wiig":http://blog.qt.digia.com/blog/author/jbache/ wrote an article called "Using Blur Behind on Windows":http://blog.qt.digia.com/blog/2009/09/15/using-blur-behind-on-windows/. At the time, I think it was posted in the Labs section, but now, the only remains I can find is the the given blog link.
I have the original source code and I have been using it with Qt 4. Now, I want to upgrade to Qt 5. Have anyone rewritten this source code for Qt 5? Or better yet, are there better and simpler ways of doing this with Qt 5.
For those who don't have the original article and source code; the goal is to extend the "glass"-like window borders into the window itself, using the Aero effects on Windows.
-
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.
-
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.