Fix emulate message bug and git modules, window part mistakes, non client leave mistakes and crash (#269)

* Fix emulate message bug and git modules

* Fix window part mistakes

* Fix WM_NCMOUSELEAVE message mistakes and crash

* Fix maximize minimize button hover problem
This commit is contained in:
SineStriker 2023-08-26 12:18:36 +08:00 committed by GitHub
parent bdb1b5d68f
commit 6ff0bc9300
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 74 additions and 30 deletions

2
.gitmodules vendored
View File

@ -1,3 +1,3 @@
[submodule "cmake"] [submodule "cmake"]
path = cmake path = cmake
url = ../cmake-utils.git url = ../../wangwenx190/cmake-utils.git

View File

@ -313,9 +313,17 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
} }
const UINT uMsg = msg->message; const UINT uMsg = msg->message;
// WM_QUIT won't be posted to the WindowProc function. // WM_QUIT won't be posted to the WindowProc function.
if ((uMsg == WM_CLOSE) || (uMsg == WM_DESTROY)) { switch (uMsg) {
case WM_CLOSE:
case WM_DESTROY:
case WM_NCDESTROY:
case 144:
case 626:
return false; return false;
default:
break;
} }
const auto it = g_framelessWin32HelperData()->data.find(windowId); const auto it = g_framelessWin32HelperData()->data.find(windowId);
if (it == g_framelessWin32HelperData()->data.end()) { if (it == g_framelessWin32HelperData()->data.end()) {
return false; return false;
@ -345,19 +353,20 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
#endif // (QT_VERSION < QT_VERSION_CHECK(6, 5, 1)) #endif // (QT_VERSION < QT_VERSION_CHECK(6, 5, 1))
const auto emulateClientAreaMessage = [hWnd, uMsg, wParam, lParam](const std::optional<int> overrideMessage = std::nullopt) -> void { const auto emulateClientAreaMessage = [hWnd, uMsg, wParam, lParam](const std::optional<int> overrideMessage = std::nullopt) -> void {
const auto wparam = [uMsg, wParam]() -> WPARAM { auto myMsg = overrideMessage.value_or(uMsg);
if (uMsg == WM_NCMOUSELEAVE) { const auto wparam = [myMsg, wParam]() -> WPARAM {
if (myMsg == WM_NCMOUSELEAVE) {
return kMessageTag; return kMessageTag;
} }
const quint64 keyState = Utils::getKeyState(); const quint64 keyState = Utils::getKeyState();
if ((uMsg >= WM_NCXBUTTONDOWN) && (uMsg <= WM_NCXBUTTONDBLCLK)) { if ((myMsg >= WM_NCXBUTTONDOWN) && (myMsg <= WM_NCXBUTTONDBLCLK)) {
const auto xButtonMask = GET_XBUTTON_WPARAM(wParam); const auto xButtonMask = GET_XBUTTON_WPARAM(wParam);
return MAKEWPARAM(keyState, xButtonMask); return MAKEWPARAM(keyState, xButtonMask);
} }
return keyState; return keyState;
}(); }();
const auto lparam = [uMsg, lParam, hWnd]() -> LPARAM { const auto lparam = [myMsg, lParam, hWnd]() -> LPARAM {
if (uMsg == WM_NCMOUSELEAVE) { if (myMsg == WM_NCMOUSELEAVE) {
return 0; return 0;
} }
const auto screenPos = POINT{ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; const auto screenPos = POINT{ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
@ -373,7 +382,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
#else #else
# define SEND_MESSAGE ::PostMessageW # define SEND_MESSAGE ::PostMessageW
#endif #endif
switch (overrideMessage.value_or(uMsg)) { switch (myMsg) {
case WM_NCHITTEST: // Treat hit test messages as mouse move events. case WM_NCHITTEST: // Treat hit test messages as mouse move events.
case WM_NCMOUSEMOVE: case WM_NCMOUSEMOVE:
SEND_MESSAGE(hWnd, WM_MOUSEMOVE, wparam, lparam); SEND_MESSAGE(hWnd, WM_MOUSEMOVE, wparam, lparam);
@ -785,14 +794,12 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
const auto hitTestRecorder = qScopeGuard([&muData, &result](){ const auto hitTestRecorder = qScopeGuard([&muData, &result](){
auto &first = std::get<0>(muData.hitTestResult); auto &first = std::get<0>(muData.hitTestResult);
auto &second = std::get<1>(muData.hitTestResult); auto &second = std::get<1>(muData.hitTestResult);
if (first.has_value()) {
if (second.has_value()) { if (second.has_value()) {
std::swap(first, second); first = second;
} else if (!first.has_value()){
first = HTNOWHERE;
} }
second = *result; second = *result;
} else {
first = *result;
}
}); });
const auto nativeGlobalPos = POINT{ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; const auto nativeGlobalPos = POINT{ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
@ -999,36 +1006,73 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
// The following code is not workaround anything, it's just try to emulate the original // The following code is not workaround anything, it's just try to emulate the original
// behavior of a native Win32 window. // behavior of a native Win32 window.
const WindowPart previousWindowPart = getHittedWindowPart(data.hitTestResult.first.value_or(HTNOWHERE)); const WindowPart previousWindowPart = getHittedWindowPart(data.hitTestResult.first.value_or(HTNOWHERE));
const bool isXButtonMessage = ((uMsg >= WM_NCXBUTTONDOWN) && (uMsg <= WM_NCXBUTTONDBLCLK)); const WindowPart currentWindowPart = getHittedWindowPart(data.hitTestResult.second.value_or(HTNOWHERE));
const WindowPart nowWindowPart = getHittedWindowPart(isXButtonMessage ? GET_NCHITTEST_WPARAM(wParam) : wParam);
if (uMsg == WM_NCMOUSELEAVE) { if (uMsg == WM_NCMOUSELEAVE) {
if (nowWindowPart == WindowPart::NotInterested) {
std::ignore = listenForMouseLeave(hWnd, false);
}
if (previousWindowPart == WindowPart::ChromeButton) { if (previousWindowPart == WindowPart::ChromeButton) {
if (nowWindowPart == WindowPart::ClientArea) { if (currentWindowPart == WindowPart::ClientArea) {
*result = FALSE; // Since we filter the WM_MOUSELEAVE event when the mouse is above the buttons,
// Qt will always think it's tracking the mouse.
// If we don't track the mouse after the mouse enter client area, there will
// be no WM_MOUSELEAVE sent by Windows which should be sent.
std::ignore = listenForMouseLeave(hWnd, false);
// According to numerous experiments we've conducted, Windows maintains the mouse
// state internally, we must eventually let Windows handle the WM_NCMOUSELEAVE
// otherwise the internal state will be broken so that Windows may fail to send
// the WM_NCMOUSELEAVE messages which we need.
*result = ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
return true; return true;
} else if (nowWindowPart == WindowPart::NotInterested) { } else if (currentWindowPart == WindowPart::NotInterested) {
emulateClientAreaMessage(WM_NCMOUSELEAVE); emulateClientAreaMessage(WM_NCMOUSELEAVE);
} }
} }
if (currentWindowPart == WindowPart::NotInterested) {
// The mouse is leaving window from non-client area, clear window part caches
auto &hitTestResult = muData.hitTestResult;
hitTestResult.first.reset();
hitTestResult.second.reset();
// Notice: we're not going to clear window part caches when the mouse leaves window
// from client area, which means we will get previous window part as HTCLIENT if
// the mouse leaves window from client area and enters window from non-client area,
// but it has no bad effect.
}
} else { } else {
if (uMsg == WM_NCMOUSEMOVE) { if (uMsg == WM_NCMOUSEMOVE) {
if (nowWindowPart != WindowPart::ChromeButton) { if (currentWindowPart != WindowPart::ChromeButton) {
data.params.resetQtGrabbedControl(); data.params.resetQtGrabbedControl();
} }
if ((previousWindowPart == WindowPart::ChromeButton) if ((previousWindowPart == WindowPart::ChromeButton)
&& ((nowWindowPart == WindowPart::TitleBar) && ((currentWindowPart == WindowPart::TitleBar)
|| (nowWindowPart == WindowPart::ResizeBorder) || (currentWindowPart == WindowPart::ResizeBorder)
|| (nowWindowPart == WindowPart::FixedBorder))) { || (currentWindowPart == WindowPart::FixedBorder))) {
emulateClientAreaMessage(WM_NCMOUSELEAVE); emulateClientAreaMessage(WM_NCMOUSELEAVE);
} }
} }
if (nowWindowPart == WindowPart::ChromeButton) {
{
// We need to make sure we get the correct window part when a WM_NCMOUSELEAVE come,
// so we reset current window part to null when we receive a WM_NCMOUSEMOVE.
// If the mouse is entering the client area, there must be a WM_NCHITTEST setting current
// window part to NCCLIENT before the WM_NCMOUSELEAVE comes;
// If the mouse is leaving the window, current window part remains as null.
auto &hitTestResult = muData.hitTestResult;
if (hitTestResult.second.has_value()) {
hitTestResult.first = hitTestResult.second;
hitTestResult.second.reset();
}
}
if (currentWindowPart == WindowPart::ChromeButton) {
emulateClientAreaMessage(); emulateClientAreaMessage();
std::ignore = listenForMouseLeave(hWnd, true); if (uMsg == WM_NCMOUSEMOVE) {
*result = (isXButtonMessage ? TRUE : FALSE); // We should pass WM_NCMOUSEMOVE to Windows as well as WM_NCMOUSELEAVE
*result = ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
} else {
*result = ((uMsg >= WM_NCXBUTTONDOWN) && (uMsg <= WM_NCXBUTTONDBLCLK)) ? TRUE : FALSE;
}
return true; return true;
} }
} }