From d1d39c9ca992b070a1214016fdd7150626a0fb34 Mon Sep 17 00:00:00 2001 From: weijialu <120215034@qq.com> Date: Tue, 7 Jan 2020 10:33:15 +0800 Subject: [PATCH 1/2] fix issue #13 when there is a widget that has a attribute Qt::WA_NativeWindow inside the mainwindow, QWinWidget will never receive any native event. to fix this, install a native event filter for QWinWidget to force it to get the native event. --- TrueFramelessWindow.pro | 3 +- qwinwidget.cpp | 272 +++++++++++++++++++++------------------- qwinwidget.h | 17 ++- 3 files changed, 157 insertions(+), 135 deletions(-) diff --git a/TrueFramelessWindow.pro b/TrueFramelessWindow.pro index 07338ae..7404016 100644 --- a/TrueFramelessWindow.pro +++ b/TrueFramelessWindow.pro @@ -28,7 +28,8 @@ win32 { LIBS += -L"C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib" \ -ldwmapi \ - -lgdi32 + -lgdi32 \ + -lUser32 } mac { diff --git a/qwinwidget.cpp b/qwinwidget.cpp index f27c5f4..66457ab 100644 --- a/qwinwidget.cpp +++ b/qwinwidget.cpp @@ -76,6 +76,146 @@ */ +NativeEventFilter::NativeEventFilter(QWinWidget *widget) + : winWidget(widget) +{ + +} + +bool NativeEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *result) +{ + if (eventType != "windows_generic_MSG" && eventType != "windows_dispatcher_MSG") + return false; + + MSG *msg = (MSG *)message; + + if (msg->message == WM_SETFOCUS) + { + Qt::FocusReason reason; + if (::GetKeyState(VK_LBUTTON) < 0 || ::GetKeyState(VK_RBUTTON) < 0) + reason = Qt::MouseFocusReason; + else if (::GetKeyState(VK_SHIFT) < 0) + reason = Qt::BacktabFocusReason; + else + reason = Qt::TabFocusReason; + QFocusEvent e(QEvent::FocusIn, reason); + QApplication::sendEvent(winWidget, &e); + } + + //Only close if safeToClose clears() + if (msg->message == WM_CLOSE) + { + if (true /* put your check for it if it safe to close your app here */) //eg, does the user need to save a document + { + //Safe to close, so hide the parent window + ShowWindow(winWidget->m_ParentNativeWindowHandle, false); + + //And then quit + QApplication::quit(); + } + else + { + *result = 0; //Set the message to 0 to ignore it, and thus, don't actually close + return true; + } + } + + //Double check WM_SIZE messages to see if the parent native window is maximized + if (msg->message == WM_SIZE) + { + if (winWidget->p_Widget && winWidget->p_Widget->maximizeButton) + { + //Get the window state + WINDOWPLACEMENT wp; + GetWindowPlacement(winWidget->m_ParentNativeWindowHandle, &wp); + + //If we're maximized, + if (wp.showCmd == SW_MAXIMIZE) + { + //Maximize button should show as Restore + winWidget->p_Widget->maximizeButton->setChecked(true); + } + else + { + //Maximize button should show as Maximize + winWidget->p_Widget->maximizeButton->setChecked(false); + } + } + } + + //Pass NCHITTESTS on the window edges as determined by BORDERWIDTH & TOOLBARHEIGHT through to the parent native window + if (msg->message == WM_NCHITTEST) + { + RECT WindowRect; + int x, y; + + GetWindowRect(msg->hwnd, &WindowRect); + x = GET_X_LPARAM(msg->lParam) - WindowRect.left; + y = GET_Y_LPARAM(msg->lParam) - WindowRect.top; + + if (x >= winWidget->BORDERWIDTH && x <= WindowRect.right - WindowRect.left - winWidget->BORDERWIDTH && y >= winWidget->BORDERWIDTH && y <= winWidget->TOOLBARHEIGHT) + { + if (winWidget->p_Widget->toolBar) + { + //If the mouse is over top of the toolbar area BUT is actually positioned over a child widget of the toolbar, + //Then we don't want to enable dragging. This allows for buttons in the toolbar, eg, a Maximize button, to keep the mouse interaction + if (QApplication::widgetAt(QCursor::pos()) != winWidget->p_Widget->toolBar) + return false; + } + + //The mouse is over the toolbar area & is NOT over a child of the toolbar, so pass this message + //through to the native window for HTCAPTION dragging + *result = HTTRANSPARENT; + return true; + + } + else if (x < winWidget->BORDERWIDTH && y < winWidget->BORDERWIDTH) + { + *result = HTTRANSPARENT; + return true; + } + else if (x > WindowRect.right - WindowRect.left - winWidget->BORDERWIDTH && y < winWidget->BORDERWIDTH) + { + *result = HTTRANSPARENT; + return true; + } + else if (x > WindowRect.right - WindowRect.left - winWidget->BORDERWIDTH && y > WindowRect.bottom - WindowRect.top - winWidget->BORDERWIDTH) + { + *result = HTTRANSPARENT; + return true; + } + else if (x < winWidget->BORDERWIDTH && y > WindowRect.bottom - WindowRect.top - winWidget->BORDERWIDTH) + { + *result = HTTRANSPARENT; + return true; + } + else if (x < winWidget->BORDERWIDTH) + { + *result = HTTRANSPARENT; + return true; + } + else if (y < winWidget->BORDERWIDTH) + { + *result = HTTRANSPARENT; + return true; + } + else if (x > WindowRect.right - WindowRect.left - winWidget->BORDERWIDTH) + { + *result = HTTRANSPARENT; + return true; + } + else if (y > WindowRect.bottom - WindowRect.top - winWidget->BORDERWIDTH) + { + *result = HTTRANSPARENT; + return true; + } + + return false; + } + + return false; +} + QWinWidget::QWinWidget() : QWidget(nullptr), m_Layout(), @@ -84,6 +224,7 @@ QWinWidget::QWinWidget() _prevFocus(nullptr), _reenableParent(false) { + qApp->installNativeEventFilter(new NativeEventFilter(this)); //Create a native window and give it geometry values * devicePixelRatio for HiDPI support p_ParentWinNativeWindow = new WinNativeWindow(1 * window()->devicePixelRatio() @@ -314,137 +455,6 @@ void QWinWidget::onCloseButtonClicked() } } -bool QWinWidget::nativeEvent(const QByteArray &, void *message, long *result) -{ - MSG *msg = (MSG *)message; - - if (msg->message == WM_SETFOCUS) - { - Qt::FocusReason reason; - if (::GetKeyState(VK_LBUTTON) < 0 || ::GetKeyState(VK_RBUTTON) < 0) - reason = Qt::MouseFocusReason; - else if (::GetKeyState(VK_SHIFT) < 0) - reason = Qt::BacktabFocusReason; - else - reason = Qt::TabFocusReason; - QFocusEvent e(QEvent::FocusIn, reason); - QApplication::sendEvent(this, &e); - } - - //Only close if safeToClose clears() - if (msg->message == WM_CLOSE) - { - if (true /* put your check for it if it safe to close your app here */) //eg, does the user need to save a document - { - //Safe to close, so hide the parent window - ShowWindow(m_ParentNativeWindowHandle, false); - - //And then quit - QApplication::quit(); - } - else - { - *result = 0; //Set the message to 0 to ignore it, and thus, don't actually close - return true; - } - } - - //Double check WM_SIZE messages to see if the parent native window is maximized - if (msg->message == WM_SIZE) - { - if (p_Widget && p_Widget->maximizeButton) - { - //Get the window state - WINDOWPLACEMENT wp; - GetWindowPlacement(m_ParentNativeWindowHandle, &wp); - - //If we're maximized, - if (wp.showCmd == SW_MAXIMIZE) - { - //Maximize button should show as Restore - p_Widget->maximizeButton->setChecked(true); - } - else - { - //Maximize button should show as Maximize - p_Widget->maximizeButton->setChecked(false); - } - } - } - - //Pass NCHITTESTS on the window edges as determined by BORDERWIDTH & TOOLBARHEIGHT through to the parent native window - if (msg->message == WM_NCHITTEST) - { - RECT WindowRect; - int x, y; - - GetWindowRect(msg->hwnd, &WindowRect); - x = GET_X_LPARAM(msg->lParam) - WindowRect.left; - y = GET_Y_LPARAM(msg->lParam) - WindowRect.top; - - if (x >= BORDERWIDTH && x <= WindowRect.right - WindowRect.left - BORDERWIDTH && y >= BORDERWIDTH && y <= TOOLBARHEIGHT) - { - if (p_Widget->toolBar) - { - //If the mouse is over top of the toolbar area BUT is actually positioned over a child widget of the toolbar, - //Then we don't want to enable dragging. This allows for buttons in the toolbar, eg, a Maximize button, to keep the mouse interaction - if (QApplication::widgetAt(QCursor::pos()) != p_Widget->toolBar) - return false; - } - - //The mouse is over the toolbar area & is NOT over a child of the toolbar, so pass this message - //through to the native window for HTCAPTION dragging - *result = HTTRANSPARENT; - return true; - - } - else if (x < BORDERWIDTH && y < BORDERWIDTH) - { - *result = HTTRANSPARENT; - return true; - } - else if (x > WindowRect.right - WindowRect.left - BORDERWIDTH && y < BORDERWIDTH) - { - *result = HTTRANSPARENT; - return true; - } - else if (x > WindowRect.right - WindowRect.left - BORDERWIDTH && y > WindowRect.bottom - WindowRect.top - BORDERWIDTH) - { - *result = HTTRANSPARENT; - return true; - } - else if (x < BORDERWIDTH && y > WindowRect.bottom - WindowRect.top - BORDERWIDTH) - { - *result = HTTRANSPARENT; - return true; - } - else if (x < BORDERWIDTH) - { - *result = HTTRANSPARENT; - return true; - } - else if (y < BORDERWIDTH) - { - *result = HTTRANSPARENT; - return true; - } - else if (x > WindowRect.right - WindowRect.left - BORDERWIDTH) - { - *result = HTTRANSPARENT; - return true; - } - else if (y > WindowRect.bottom - WindowRect.top - BORDERWIDTH) - { - *result = HTTRANSPARENT; - return true; - } - - return false; - } - - return false; -} - /*! \reimp */ diff --git a/qwinwidget.h b/qwinwidget.h index f79aa2b..e515857 100644 --- a/qwinwidget.h +++ b/qwinwidget.h @@ -53,10 +53,23 @@ #include #include +#include #include "widget.h" #include "WinNativeWindow.h" +class QWinWidget; + +class NativeEventFilter : public QAbstractNativeEventFilter +{ +public: + explicit NativeEventFilter(QWinWidget *widget); + bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override; + +private: + QWinWidget *winWidget; +}; + class QWinWidget : public QWidget { Q_OBJECT @@ -83,8 +96,6 @@ public slots: bool focusNextPrevChild(bool next) override; void focusInEvent(QFocusEvent *e) override; - bool nativeEvent(const QByteArray &eventType, void *message, long *result) override; - private: QVBoxLayout m_Layout; @@ -104,7 +115,7 @@ public slots: void resetFocus(); - + friend class NativeEventFilter; }; #endif // QWINWIDGET_H From a6ab848397898732e3853fdd313958ec37035afa Mon Sep 17 00:00:00 2001 From: weijialu <120215034@qq.com> Date: Tue, 7 Jan 2020 16:36:37 +0800 Subject: [PATCH 2/2] fix QWinWidget multi instance issue. click close button just class QWinWidget's close, not just do QApplication::quit. --- qwinwidget.cpp | 35 +++++++++-------------------------- qwinwidget.h | 1 + winnativewindow.cpp | 28 +++++++++++++--------------- winnativewindow.h | 4 ++-- 4 files changed, 25 insertions(+), 43 deletions(-) diff --git a/qwinwidget.cpp b/qwinwidget.cpp index 66457ab..d881ca1 100644 --- a/qwinwidget.cpp +++ b/qwinwidget.cpp @@ -105,19 +105,7 @@ bool NativeEventFilter::nativeEventFilter(const QByteArray &eventType, void *mes //Only close if safeToClose clears() if (msg->message == WM_CLOSE) { - if (true /* put your check for it if it safe to close your app here */) //eg, does the user need to save a document - { - //Safe to close, so hide the parent window - ShowWindow(winWidget->m_ParentNativeWindowHandle, false); - - //And then quit - QApplication::quit(); - } - else - { - *result = 0; //Set the message to 0 to ignore it, and thus, don't actually close - return true; - } + //do nothing } //Double check WM_SIZE messages to see if the parent native window is maximized @@ -302,7 +290,6 @@ QWinWidget::QWinWidget() //Send the parent native window a WM_SIZE message to update the widget size SendMessage(m_ParentNativeWindowHandle, WM_SIZE, 0, 0); - } @@ -441,23 +428,19 @@ void QWinWidget::onMaximizeButtonClicked() void QWinWidget::onCloseButtonClicked() { - if(true /* put your check for it if it safe to close your app here */) //eg, does the user need to save a document - { - //Safe to close, so hide the parent window - ShowWindow(m_ParentNativeWindowHandle, false); - - //And then quit - QApplication::quit(); - } - else - { - //Do nothing, and thus, don't actually close the window - } + close(); } /*! \reimp */ + +void QWinWidget::closeEvent(QCloseEvent *event) +{ + //use closeEvent to check for it whether it is safe to close your app here or not + ShowWindow(m_ParentNativeWindowHandle, false); +} + bool QWinWidget::eventFilter(QObject *o, QEvent *e) { QWidget *w = (QWidget*)o; diff --git a/qwinwidget.h b/qwinwidget.h index e515857..3dc5eb2 100644 --- a/qwinwidget.h +++ b/qwinwidget.h @@ -90,6 +90,7 @@ public slots: void onCloseButtonClicked(); protected: + void closeEvent(QCloseEvent *event) override; void childEvent( QChildEvent *e ) override; bool eventFilter( QObject *o, QEvent *e ) override; diff --git a/winnativewindow.cpp b/winnativewindow.cpp index 9886ddb..1f4ed1e 100644 --- a/winnativewindow.cpp +++ b/winnativewindow.cpp @@ -3,12 +3,10 @@ #include #include - -HWND WinNativeWindow::childWindow = nullptr; -QWidget* WinNativeWindow::childWidget = nullptr; - WinNativeWindow::WinNativeWindow(const int x, const int y, const int width, const int height) : hWnd(nullptr) + , childWindow(nullptr) + , childWidget(nullptr) { //The native window technically has a background color. You can set it here @@ -96,9 +94,9 @@ LRESULT CALLBACK WinNativeWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam //If the parent window gets any close messages, send them over to QWinWidget and don't actually close here case WM_CLOSE: { - if (childWindow) + if (window->childWindow) { - SendMessage(childWindow, WM_CLOSE, 0, 0); + SendMessage(window->childWindow, WM_CLOSE, 0, 0); return 0; } break; @@ -112,7 +110,7 @@ LRESULT CALLBACK WinNativeWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam case WM_NCHITTEST: { - const LONG borderWidth = 8 * childWidget->window()->devicePixelRatio(); //This value can be arbitrarily large as only intentionally-HTTRANSPARENT'd messages arrive here + const LONG borderWidth = 8 * window->childWidget->window()->devicePixelRatio(); //This value can be arbitrarily large as only intentionally-HTTRANSPARENT'd messages arrive here RECT winrect; GetWindowRect(hWnd, &winrect); long x = GET_X_LPARAM(lParam); @@ -178,19 +176,19 @@ LRESULT CALLBACK WinNativeWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam WINDOWPLACEMENT wp; wp.length = sizeof(WINDOWPLACEMENT); GetWindowPlacement(hWnd, &wp); - if (childWidget) + if (window->childWidget) { if (wp.showCmd == SW_MAXIMIZE) { - childWidget->setGeometry(8, 8 //Maximized window draw 8 pixels off screen - , winrect.right / childWidget->window()->devicePixelRatio() - 16 - , winrect.bottom / childWidget->window()->devicePixelRatio() - 16); + window->childWidget->setGeometry(8, 8 //Maximized window draw 8 pixels off screen + , winrect.right / window->childWidget->window()->devicePixelRatio() - 16 + , winrect.bottom / window->childWidget->window()->devicePixelRatio() - 16); } else { - childWidget->setGeometry(0, 0 - , winrect.right / childWidget->window()->devicePixelRatio() - , winrect.bottom / childWidget->window()->devicePixelRatio()); + window->childWidget->setGeometry(0, 0 + , winrect.right / window->childWidget->window()->devicePixelRatio() + , winrect.bottom / window->childWidget->window()->devicePixelRatio()); } } @@ -201,7 +199,7 @@ LRESULT CALLBACK WinNativeWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam { MINMAXINFO* minMaxInfo = (MINMAXINFO*)lParam; if (window->minimumSize.required) { - minMaxInfo->ptMinTrackSize.x = window->getMinimumWidth();; + minMaxInfo->ptMinTrackSize.x = window->getMinimumWidth(); minMaxInfo->ptMinTrackSize.y = window->getMinimumHeight(); } diff --git a/winnativewindow.h b/winnativewindow.h index ce13019..675c3ad 100644 --- a/winnativewindow.h +++ b/winnativewindow.h @@ -31,8 +31,8 @@ class WinNativeWindow HWND hWnd; - static HWND childWindow; - static QWidget* childWidget; + HWND childWindow; + QWidget* childWidget; private: