Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2020-04-01 15:58:50 +08:00
parent d1358c51f9
commit 5e3a3b7109
2 changed files with 51 additions and 47 deletions

View File

@ -190,11 +190,15 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
} else if (!m_framelessWindows.contains(msg->hwnd)) { } else if (!m_framelessWindows.contains(msg->hwnd)) {
return false; return false;
} }
if (!m_windowData.contains(msg->hwnd)) { LPWINDOW data = nullptr;
// We have to record every window's state. const auto userData = reinterpret_cast<LPWINDOW>(GetWindowLongPtrW(msg->hwnd, GWLP_USERDATA));
m_windowData.insert(msg->hwnd, qMakePair(FALSE, FALSE)); if (userData) {
data = userData;
} else {
init(msg->hwnd); init(msg->hwnd);
data = reinterpret_cast<LPWINDOW>(GetWindowLongPtrW(msg->hwnd, GWLP_USERDATA));
} }
Q_ASSERT(data);
switch (msg->message) { switch (msg->message) {
case WM_NCCREATE: { case WM_NCCREATE: {
// Work-around a long-existing Windows bug. // Work-around a long-existing Windows bug.
@ -269,7 +273,7 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
case WM_DWMCOMPOSITIONCHANGED: { case WM_DWMCOMPOSITIONCHANGED: {
// Bring the frame shadow back through DWM. // Bring the frame shadow back through DWM.
// Don't paint the shadow manually using QPainter or QGraphicsEffect. // Don't paint the shadow manually using QPainter or QGraphicsEffect.
handleDwmCompositionChanged(msg->hwnd); handleDwmCompositionChanged(data);
*result = 0; *result = 0;
return true; return true;
} }
@ -282,7 +286,7 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
return true; return true;
} }
case WM_NCPAINT: { case WM_NCPAINT: {
if (m_windowData.value(msg->hwnd).first) { if (data->dwmCompositionEnabled) {
break; break;
} else { } else {
// Only block WM_NCPAINT when composition is disabled. If it's // Only block WM_NCPAINT when composition is disabled. If it's
@ -379,8 +383,8 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
// composition and theming are disabled. These messages don't paint // composition and theming are disabled. These messages don't paint
// when composition is enabled and blocking WM_NCUAHDRAWCAPTION should // when composition is enabled and blocking WM_NCUAHDRAWCAPTION should
// be enough to prevent painting when theming is enabled. // be enough to prevent painting when theming is enabled.
if (!m_windowData.value(msg->hwnd).first && if (!data->dwmCompositionEnabled &&
!m_windowData.value(msg->hwnd).second) { !data->themeEnabled) {
const LONG_PTR oldStyle = GetWindowLongPtrW(msg->hwnd, GWL_STYLE); const LONG_PTR oldStyle = GetWindowLongPtrW(msg->hwnd, GWL_STYLE);
// Prevent Windows from drawing the default title bar by temporarily // Prevent Windows from drawing the default title bar by temporarily
// toggling the WS_VISIBLE style. // toggling the WS_VISIBLE style.
@ -394,7 +398,7 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
break; break;
} }
case WM_THEMECHANGED: { case WM_THEMECHANGED: {
handleThemeChanged(msg->hwnd); handleThemeChanged(data);
break; break;
} }
case WM_WINDOWPOSCHANGED: { case WM_WINDOWPOSCHANGED: {
@ -408,6 +412,9 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
} }
void WinNativeEventFilter::init(HWND handle) { void WinNativeEventFilter::init(HWND handle) {
LPWINDOW data = new WINDOW;
data->hWnd = handle;
SetWindowLongPtrW(handle, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(data));
// Make sure our window is a normal application window, we'll remove the // Make sure our window is a normal application window, we'll remove the
// window frame later in Win32 events, don't use WS_POPUP to do this. // window frame later in Win32 events, don't use WS_POPUP to do this.
SetWindowLongPtrW(handle, GWL_STYLE, SetWindowLongPtrW(handle, GWL_STYLE,
@ -419,8 +426,8 @@ void WinNativeEventFilter::init(HWND handle) {
// Direct2D, Direct3D, DirectComposition, etc. // Direct2D, Direct3D, DirectComposition, etc.
SetLayeredWindowAttributes(handle, RGB(255, 0, 255), 0, LWA_COLORKEY); SetLayeredWindowAttributes(handle, RGB(255, 0, 255), 0, LWA_COLORKEY);
// Make sure our window has the frame shadow. // Make sure our window has the frame shadow.
handleDwmCompositionChanged(handle); handleDwmCompositionChanged(data);
handleThemeChanged(handle); handleThemeChanged(data);
// For debug purposes. // For debug purposes.
qDebug().noquote() << "Window handle:" << handle; qDebug().noquote() << "Window handle:" << handle;
qDebug().noquote() << "Window DPI:" << windowDpi(handle) qDebug().noquote() << "Window DPI:" << windowDpi(handle)
@ -430,11 +437,10 @@ void WinNativeEventFilter::init(HWND handle) {
<< "Window titlebar height:" << titlebarHeight(handle); << "Window titlebar height:" << titlebarHeight(handle);
} }
void WinNativeEventFilter::handleDwmCompositionChanged(HWND handle) { void WinNativeEventFilter::handleDwmCompositionChanged(LPWINDOW data) {
BOOL enabled = FALSE; BOOL enabled = FALSE;
DwmIsCompositionEnabled(&enabled); DwmIsCompositionEnabled(&enabled);
const BOOL theme = m_windowData.value(handle).second; data->dwmCompositionEnabled = enabled;
m_windowData[handle] = qMakePair(enabled, theme);
// We should not draw the frame shadow if DWM composition is disabled, in // We should not draw the frame shadow if DWM composition is disabled, in
// other words, a window should not have frame shadow when Windows Aero is // other words, a window should not have frame shadow when Windows Aero is
// not enabled. // not enabled.
@ -444,27 +450,25 @@ void WinNativeEventFilter::handleDwmCompositionChanged(HWND handle) {
// The frame shadow is drawn on the non-client area and thus we have to // The frame shadow is drawn on the non-client area and thus we have to
// make sure the non-client area rendering is enabled first. // make sure the non-client area rendering is enabled first.
const DWMNCRENDERINGPOLICY ncrp = DWMNCRP_ENABLED; const DWMNCRENDERINGPOLICY ncrp = DWMNCRP_ENABLED;
DwmSetWindowAttribute(handle, DWMWA_NCRENDERING_POLICY, &ncrp, DwmSetWindowAttribute(data->hWnd, DWMWA_NCRENDERING_POLICY, &ncrp,
sizeof(ncrp)); sizeof(ncrp));
// Negative margins have special meaning to // Negative margins have special meaning to
// DwmExtendFrameIntoClientArea. Negative margins create the "sheet of // DwmExtendFrameIntoClientArea. Negative margins create the "sheet of
// glass" effect, where the client area is rendered as a solid surface // glass" effect, where the client area is rendered as a solid surface
// with no window border. // with no window border.
const MARGINS margins = {-1, -1, -1, -1}; const MARGINS margins = {-1, -1, -1, -1};
DwmExtendFrameIntoClientArea(handle, &margins); DwmExtendFrameIntoClientArea(data->hWnd, &margins);
} }
// handleBlurForWindow(handle, enabled); // handleBlurForWindow(data);
refreshWindow(handle); refreshWindow(data->hWnd);
} }
void WinNativeEventFilter::handleThemeChanged(HWND handle) { void WinNativeEventFilter::handleThemeChanged(LPWINDOW data) {
const BOOL dwm = m_windowData.value(handle).first; data->themeEnabled = IsThemeActive();
m_windowData[handle] = qMakePair(dwm, IsThemeActive());
} }
void WinNativeEventFilter::handleBlurForWindow(HWND handle, void WinNativeEventFilter::handleBlurForWindow(LPWINDOW data) {
BOOL compositionEnabled) { if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows7) {
if (!handle || (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows7)) {
return; return;
} }
// We prefer using DWM blur on Windows 7 because it has better appearance. // We prefer using DWM blur on Windows 7 because it has better appearance.
@ -472,41 +476,41 @@ void WinNativeEventFilter::handleBlurForWindow(HWND handle,
// Windows Aero // Windows Aero
DWM_BLURBEHIND dwmbb; DWM_BLURBEHIND dwmbb;
dwmbb.dwFlags = DWM_BB_ENABLE; dwmbb.dwFlags = DWM_BB_ENABLE;
dwmbb.fEnable = compositionEnabled; dwmbb.fEnable = data->dwmCompositionEnabled;
dwmbb.hRgnBlur = nullptr; dwmbb.hRgnBlur = nullptr;
dwmbb.fTransitionOnMaximized = FALSE; dwmbb.fTransitionOnMaximized = FALSE;
DwmEnableBlurBehindWindow(handle, &dwmbb); DwmEnableBlurBehindWindow(data->hWnd, &dwmbb);
} else if (m_SetWindowCompositionAttribute) { } else if (m_SetWindowCompositionAttribute) {
ACCENT_POLICY accent; ACCENT_POLICY accentPolicy;
accent.AccentFlags = 0; accentPolicy.AccentFlags = 0;
// GradientColor only has effect when using with acrylic, so we can set it to zero in most cases. // GradientColor only has effect when using with acrylic, so we can set it to zero in most cases.
// It's an AGBR unsigned int, for example, use 0xCC000000 for dark blur behind background. // It's an AGBR unsigned int, for example, use 0xCC000000 for dark blur behind background.
accent.GradientColor = 0; accentPolicy.GradientColor = 0;
accent.AnimationId = 0; accentPolicy.AnimationId = 0;
WINDOWCOMPOSITIONATTRIBDATA data; WINDOWCOMPOSITIONATTRIBDATA attribData;
data.dwAttribute = WCA_ACCENT_POLICY; attribData.dwAttribute = WCA_ACCENT_POLICY;
data.pvAttribute = &accent; attribData.pvAttribute = &accentPolicy;
data.cbAttribute = sizeof(accent); attribData.cbAttribute = sizeof(accentPolicy);
if (compositionEnabled) { if (data->dwmCompositionEnabled) {
// Windows 10, version 1709 (10.0.16299) // Windows 10, version 1709 (10.0.16299)
if (QOperatingSystemVersion::current() >= if (QOperatingSystemVersion::current() >=
QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0,
16299)) { 16299)) {
// Acrylic (Will also blur but is completely different with Windows Aero) // Acrylic (Will also blur but is completely different with Windows Aero)
accent.AccentState = ACCENT_ENABLE_ACRYLICBLURBEHIND; accentPolicy.AccentState = ACCENT_ENABLE_ACRYLICBLURBEHIND;
} else if (QOperatingSystemVersion::current() >= } else if (QOperatingSystemVersion::current() >=
QOperatingSystemVersion::Windows10) { QOperatingSystemVersion::Windows10) {
// Blur (Something like Windows Aero in Windows 7) // Blur (Something like Windows Aero in Windows 7)
accent.AccentState = ACCENT_ENABLE_BLURBEHIND; accentPolicy.AccentState = ACCENT_ENABLE_BLURBEHIND;
} else if (QOperatingSystemVersion::current() >= } else if (QOperatingSystemVersion::current() >=
QOperatingSystemVersion::Windows8) { QOperatingSystemVersion::Windows8) {
// Transparent gradient color // Transparent gradient color
accent.AccentState = ACCENT_ENABLE_TRANSPARENTGRADIENT; accentPolicy.AccentState = ACCENT_ENABLE_TRANSPARENTGRADIENT;
} }
} else { } else {
accent.AccentState = ACCENT_DISABLED; accentPolicy.AccentState = ACCENT_DISABLED;
} }
m_SetWindowCompositionAttribute(handle, &data); m_SetWindowCompositionAttribute(data->hWnd, &attribData);
} }
} }

View File

@ -1,8 +1,6 @@
#pragma once #pragma once
#include <QAbstractNativeEventFilter> #include <QAbstractNativeEventFilter>
#include <QHash>
#include <QPair>
#include <QVector> #include <QVector>
#include <qt_windows.h> #include <qt_windows.h>
@ -10,6 +8,11 @@ class WinNativeEventFilter : public QAbstractNativeEventFilter {
Q_DISABLE_COPY_MOVE(WinNativeEventFilter) Q_DISABLE_COPY_MOVE(WinNativeEventFilter)
public: public:
typedef struct tagWINDOW {
HWND hWnd = nullptr;
BOOL dwmCompositionEnabled = FALSE, themeEnabled = FALSE;
} WINDOW, *LPWINDOW;
explicit WinNativeEventFilter(); explicit WinNativeEventFilter();
~WinNativeEventFilter() override; ~WinNativeEventFilter() override;
@ -51,9 +54,9 @@ public:
private: private:
void init(HWND handle); void init(HWND handle);
void handleDwmCompositionChanged(HWND handle); void handleDwmCompositionChanged(LPWINDOW data);
void handleThemeChanged(HWND handle); void handleThemeChanged(LPWINDOW data);
void handleBlurForWindow(HWND handle, BOOL compositionEnabled); void handleBlurForWindow(LPWINDOW data);
UINT getDpiForWindow(HWND handle) const; UINT getDpiForWindow(HWND handle) const;
qreal getDprForWindow(HWND handle) const; qreal getDprForWindow(HWND handle) const;
int getSystemMetricsForWindow(HWND handle, int index) const; int getSystemMetricsForWindow(HWND handle, int index) const;
@ -88,9 +91,6 @@ private:
MDT_DEFAULT = MDT_EFFECTIVE_DPI MDT_DEFAULT = MDT_EFFECTIVE_DPI
}; };
// Window handle, DwmComposition, Theme
QHash<HWND, QPair<BOOL, BOOL>> m_windowData;
const UINT m_defaultDPI = 96; const UINT m_defaultDPI = 96;
const qreal m_defaultDPR = 1.0; const qreal m_defaultDPR = 1.0;