Compare commits

...

3 Commits

Author SHA1 Message Date
SineStriker 2db829884d
Fix hover bug caused by system popup menu (#272) 2023-08-26 17:59:17 +08:00
SineStriker 102bf93b84
Improve mouse move handler (#271) 2023-08-26 16:21:48 +08:00
SineStriker 2d639071cc
Fix bugs after mouse press (#270) 2023-08-26 16:04:29 +08:00
4 changed files with 75 additions and 29 deletions

View File

@ -63,7 +63,7 @@ using SetCursorCallback = std::function<void(const QCursor &)>;
using UnsetCursorCallback = std::function<void()>;
using GetWidgetHandleCallback = std::function<QObject *()>;
using ForceChildrenRepaintCallback = std::function<void(const int)>;
using ResetQtGrabbedControlCallback = std::function<void()>;
using ResetQtGrabbedControlCallback = std::function<bool()>;
struct SystemParameters
{

View File

@ -317,8 +317,9 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
case WM_CLOSE:
case WM_DESTROY:
case WM_NCDESTROY:
case 144:
case 626:
// undocumented messages
case WM_UNREGISTER_WINDOW_SERVICES:
case WM_UAHDESTROYWINDOW:
return false;
default:
break;
@ -916,7 +917,6 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
return true;
}
*result = HTCLIENT;
return true;
} else {
if (full) {
*result = HTCLIENT;
@ -979,8 +979,16 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
return true;
}
*result = HTCLIENT;
return true;
}
return true;
}
case WM_MOUSEMOVE: {
const WindowPart previousWindowPart = getHittedWindowPart(data.hitTestResult.first.value_or(HTNOWHERE));
const WindowPart currentWindowPart = getHittedWindowPart(data.hitTestResult.second.value_or(HTNOWHERE));
if (previousWindowPart == WindowPart::ChromeButton && currentWindowPart == WindowPart::ClientArea) {
std::ignore = listenForMouseLeave(hWnd, false);
}
break;
}
case WM_NCMOUSEMOVE:
case WM_NCLBUTTONDOWN:
@ -1008,23 +1016,11 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
const WindowPart previousWindowPart = getHittedWindowPart(data.hitTestResult.first.value_or(HTNOWHERE));
const WindowPart currentWindowPart = getHittedWindowPart(data.hitTestResult.second.value_or(HTNOWHERE));
if (uMsg == WM_NCMOUSELEAVE) {
if (previousWindowPart == WindowPart::ChromeButton) {
if (currentWindowPart == WindowPart::ClientArea) {
// 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;
} else if (currentWindowPart == WindowPart::NotInterested) {
emulateClientAreaMessage(WM_NCMOUSELEAVE);
}
if (previousWindowPart == WindowPart::ChromeButton && currentWindowPart == WindowPart::NotInterested) {
// If current window part is chrome button, it indicates that we must have clicked
// the minimize button or maximize button, we also should send the client leave
// message to Qt.
emulateClientAreaMessage(WM_NCMOUSELEAVE);
}
if (currentWindowPart == WindowPart::NotInterested) {
@ -1037,11 +1033,13 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
// 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.
std::ignore = data.params.resetQtGrabbedControl();
}
} else {
if (uMsg == WM_NCMOUSEMOVE) {
if (currentWindowPart != WindowPart::ChromeButton) {
data.params.resetQtGrabbedControl();
std::ignore = data.params.resetQtGrabbedControl();
}
if ((previousWindowPart == WindowPart::ChromeButton)
&& ((currentWindowPart == WindowPart::TitleBar)
@ -1049,9 +1047,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|| (currentWindowPart == WindowPart::FixedBorder))) {
emulateClientAreaMessage(WM_NCMOUSELEAVE);
}
}
{
// 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.
@ -1068,7 +1064,6 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
if (currentWindowPart == WindowPart::ChromeButton) {
emulateClientAreaMessage();
if (uMsg == WM_NCMOUSEMOVE) {
// 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;
@ -1155,7 +1150,43 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
case WM_EXITSIZEMOVE: // Sent to a window when the user releases the mouse button (from dragging the title bar or the resize border).
updateRestoreGeometry(false);
break;
case WM_ACTIVATE: {
auto filteredWParam = LOWORD(wParam);
if (filteredWParam == WA_INACTIVE) {
if (getHittedWindowPart(data.hitTestResult.second.value_or(HTNOWHERE)) == WindowPart::ChromeButton) {
emulateClientAreaMessage(WM_NCMOUSELEAVE);
// Clear window part cache
auto &hitTestResult = muData.hitTestResult;
hitTestResult.first.reset();
hitTestResult.second.reset();
}
}
break;
}
case WM_INITMENU:{
if (getHittedWindowPart(data.hitTestResult.second.value_or(HTNOWHERE)) == WindowPart::ChromeButton) {
emulateClientAreaMessage(WM_NCMOUSELEAVE);
// Clear window part cache
auto &hitTestResult = muData.hitTestResult;
hitTestResult.first.reset();
hitTestResult.second.reset();
}
break;
}
case WM_SIZE: {
if (wParam == SIZE_MAXIMIZED || wParam == SIZE_MINIMIZED) {
if (getHittedWindowPart(data.hitTestResult.second.value_or(HTNOWHERE)) == WindowPart::ChromeButton) {
emulateClientAreaMessage(WM_NCMOUSELEAVE);
// Clear window part cache
auto &hitTestResult = muData.hitTestResult;
hitTestResult.first.reset();
hitTestResult.second.reset();
}
break;
}
if (wParam != SIZE_MAXIMIZED) {
break;
}

View File

@ -168,7 +168,7 @@ void FramelessQuickHelperPrivate::attach()
params.unsetCursor = [window]() -> void { window->unsetCursor(); };
params.getWidgetHandle = []() -> QObject * { return nullptr; };
params.forceChildrenRepaint = [this](const int delay) -> void { repaintAllChildren(delay); };
params.resetQtGrabbedControl = []() -> void {};
params.resetQtGrabbedControl = []() -> bool { return false; };
FramelessManager::instance()->addWindow(&params);

View File

@ -42,7 +42,9 @@
#include <QtGui/qwindow.h>
#include <QtGui/qpalette.h>
#include <QtGui/qcursor.h>
#include <QtGui/qevent.h>
#include <QtWidgets/qwidget.h>
#include <QtWidgets/qapplication.h>
#ifndef QWIDGETSIZE_MAX
# define QWIDGETSIZE_MAX ((1 << 24) - 1)
@ -409,7 +411,20 @@ void FramelessWidgetsHelperPrivate::attach()
params.unsetCursor = [this]() -> void { window->unsetCursor(); };
params.getWidgetHandle = [this]() -> QObject * { return window; };
params.forceChildrenRepaint = [this](const int delay) -> void { repaintAllChildren(delay); };
params.resetQtGrabbedControl = []() -> void { qt_button_down = nullptr; };
params.resetQtGrabbedControl = []() -> bool {
if (qt_button_down) {
QMouseEvent e(QEvent::MouseButtonRelease,
{-999, -999},
Qt::LeftButton,
Qt::NoButton,
QApplication::keyboardModifiers()
);
QApplication::sendEvent(qt_button_down, &e);
qt_button_down = nullptr;
return true;
}
return false;
};
FramelessManager::instance()->addWindow(&params);