win32: half implementation of max btn docking

Now the problem is how to send mouse click events to our system buttons

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2022-04-09 12:53:55 +08:00
parent c940bd5ce7
commit a57011bd11
11 changed files with 149 additions and 123 deletions

View File

@ -175,6 +175,7 @@ Q_NAMESPACE_EXPORT(FRAMELESSHELPER_CORE_API)
[[maybe_unused]] static constexpr const QSize kDefaultSystemButtonSize = {int(qRound(qreal(kDefaultTitleBarHeight) * 1.5)), kDefaultTitleBarHeight};
[[maybe_unused]] static constexpr const QSize kDefaultSystemButtonIconSize = {16, 16};
[[maybe_unused]] static constexpr const QSize kDefaultWindowSize = {160, 160};
[[maybe_unused]] static constexpr const char kUsePureQtImplFlag[] = "FRAMELESSHELPER_PURE_QT_IMPLEMENTATION";
[[maybe_unused]] static constexpr const char kForceHideFrameBorderFlag[] = "FRAMELESSHELPER_FORCE_HIDE_FRAME_BORDER";
@ -185,8 +186,6 @@ FRAMELESSHELPER_STRING_CONSTANT2(UsePureQtImplKeyPath, "Options/UsePureQtImpleme
FRAMELESSHELPER_STRING_CONSTANT2(ForceHideFrameBorderKeyPath, "Options/ForceHideFrameBorder")
FRAMELESSHELPER_STRING_CONSTANT2(ForceShowFrameBorderKeyPath, "Options/ForceShowFrameBorder")
[[maybe_unused]] static constexpr const QSize kInvalidWindowSize = {160, 160};
enum class Option : int
{
Default = 0x00000000, // Default placeholder, have no effect.
@ -273,8 +272,7 @@ enum class ButtonState : int
{
Unspecified = -1,
Hovered = 0,
Pressed = 1,
Released = 2
Pressed = 1
};
Q_ENUM_NS(ButtonState)
@ -305,6 +303,8 @@ using IsInsideTitleBarDraggableAreaCallback = std::function<bool(const QPoint &)
using GetWindowDevicePixelRatioCallback = std::function<qreal()>;
using SetSystemButtonStateCallback = std::function<void(const SystemButtonType, const ButtonState)>;
struct UserSettings
{
QPoint startupPosition = {};
@ -350,6 +350,8 @@ struct SystemParameters
GetWindowDevicePixelRatioCallback getWindowDevicePixelRatio = nullptr;
SetSystemButtonStateCallback setSystemButtonState = nullptr;
[[nodiscard]] inline bool isValid() const
{
return (windowId && getWindowFlags && setWindowFlags && getWindowSize
@ -357,7 +359,8 @@ struct SystemParameters
&& getWindowScreen && isWindowFixedSize && setWindowFixedSize
&& getWindowState && setWindowState && getWindowHandle
&& windowToScreen && screenToWindow && isInsideSystemButtons
&& isInsideTitleBarDraggableArea && getWindowDevicePixelRatio);
&& isInsideTitleBarDraggableArea && getWindowDevicePixelRatio
&& setSystemButtonState);
}
};

View File

@ -84,6 +84,7 @@ Q_SIGNALS:
void fullScreenChanged();
void fixedSizeChanged();
void frameBorderColorChanged();
void systemButtonStateChanged(const Global::SystemButtonType, const Global::ButtonState);
private:
QScopedPointer<FramelessQuickWindowPrivate> d_ptr;

View File

@ -71,6 +71,7 @@ Q_SIGNALS:
void fixedSizeChanged();
void titleBarWidgetChanged();
void systemThemeChanged();
void systemButtonStateChanged(const Global::SystemButtonType, const Global::ButtonState);
private:
QScopedPointer<FramelessWidgetsHelper> m_helper;

View File

@ -76,6 +76,7 @@ Q_SIGNALS:
void titleBarWidgetChanged();
void contentWidgetChanged();
void systemThemeChanged();
void systemButtonStateChanged(const Global::SystemButtonType, const Global::ButtonState);
private:
QScopedPointer<FramelessWidgetsHelper> m_helper;

View File

@ -72,7 +72,11 @@ endif()
target_compile_definitions(${SUB_PROJ_NAME} PRIVATE
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
QT_NO_URL_CAST_FROM_STRING
QT_NO_CAST_FROM_BYTEARRAY
QT_NO_KEYWORDS
QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
QT_NO_FOREACH
QT_USE_QSTRINGBUILDER
QT_DEPRECATED_WARNINGS
QT_DISABLE_DEPRECATED_BEFORE=0x060400

View File

@ -41,9 +41,7 @@ struct Win32HelperData
{
UserSettings settings = {};
SystemParameters params = {};
//WNDPROC originalWindowProc = nullptr;
//UINT lastEventType = 0;
//Qt::MouseButtons lastEventButtons = {};
WNDPROC originalWindowProc = nullptr;
};
struct Win32Helper
@ -61,10 +59,9 @@ FRAMELESSHELPER_STRING_CONSTANT(MonitorFromWindow)
FRAMELESSHELPER_STRING_CONSTANT(GetMonitorInfoW)
FRAMELESSHELPER_STRING_CONSTANT(ScreenToClient)
FRAMELESSHELPER_STRING_CONSTANT(GetClientRect)
//FRAMELESSHELPER_STRING_CONSTANT(GetWindowLongPtrW)
//FRAMELESSHELPER_STRING_CONSTANT(SetWindowLongPtrW)
FRAMELESSHELPER_STRING_CONSTANT(GetWindowLongPtrW)
FRAMELESSHELPER_STRING_CONSTANT(SetWindowLongPtrW)
#if 0
[[nodiscard]] static inline Qt::MouseButtons keyStateToMouseButtons(const WPARAM wParam)
{
Qt::MouseButtons result = {};
@ -86,27 +83,6 @@ FRAMELESSHELPER_STRING_CONSTANT(GetClientRect)
return result;
}
[[nodiscard]] static inline WPARAM mouseButtonsToKeyState(const Qt::MouseButtons buttons)
{
WPARAM result = 0;
if (buttons & Qt::LeftButton) {
result |= MK_LBUTTON;
}
if (buttons & Qt::MiddleButton) {
result |= MK_MBUTTON;
}
if (buttons & Qt::RightButton) {
result |= MK_RBUTTON;
}
if (buttons & Qt::XButton1) {
result |= MK_XBUTTON1;
}
if (buttons & Qt::XButton2) {
result |= MK_XBUTTON2;
}
return result;
}
[[nodiscard]] static inline Qt::MouseButtons queryMouseButtons()
{
Qt::MouseButtons result = {};
@ -144,87 +120,64 @@ FRAMELESSHELPER_STRING_CONSTANT(GetClientRect)
}
const Win32HelperData data = g_win32Helper()->data.value(windowId);
g_win32Helper()->mutex.unlock();
if (uMsg == WM_EXITSIZEMOVE) {
const Qt::MouseButtons currentButtons = queryMouseButtons();
if (currentButtons != data.lastEventButtons) {
g_win32Helper()->mutex.lock();
g_win32Helper()->data[windowId].lastEventType = 0;
g_win32Helper()->data[windowId].lastEventButtons = {};
g_win32Helper()->mutex.unlock();
}
}
const bool isNonClientMouseEvent = (((uMsg >= WM_NCMOUSEMOVE) && (uMsg <= WM_NCMBUTTONDBLCLK))
|| (uMsg == WM_NCHITTEST));
const bool isClientMouseEvent = (((uMsg >= WM_MOUSEFIRST) && (uMsg <= WM_MOUSELAST))
|| ((uMsg >= WM_XBUTTONDOWN) && (uMsg <= WM_XBUTTONDBLCLK)));
if (isNonClientMouseEvent || isClientMouseEvent) {
const Qt::MouseButtons qtButtons = (isNonClientMouseEvent ? queryMouseButtons() : keyStateToMouseButtons(wParam));
g_win32Helper()->mutex.lock();
g_win32Helper()->data[windowId].lastEventType = uMsg;
g_win32Helper()->data[windowId].lastEventButtons = qtButtons;
g_win32Helper()->mutex.unlock();
//const WPARAM nativeButtons = mouseButtonsToKeyState(qtButtons);
const POINT posFromLParam = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
const Qt::MouseButtons mouseButtons = (isNonClientMouseEvent ? queryMouseButtons() : keyStateToMouseButtons(wParam));
const POINT nativePosExtractedFromLParam = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
POINT nativeScreenPos = {};
POINT nativeClientPos = {};
POINT nativeWindowPos = {};
if (isNonClientMouseEvent) {
nativeScreenPos = posFromLParam;
nativeClientPos = nativeScreenPos;
ScreenToClient(hWnd, &nativeClientPos);
nativeScreenPos = nativePosExtractedFromLParam;
nativeWindowPos = nativeScreenPos;
ScreenToClient(hWnd, &nativeWindowPos);
}
if (isClientMouseEvent) {
nativeClientPos = posFromLParam;
nativeScreenPos = nativeClientPos;
nativeWindowPos = nativePosExtractedFromLParam;
nativeScreenPos = nativeWindowPos;
ClientToScreen(hWnd, &nativeScreenPos);
}
const LPARAM screenPosLParam = MAKELPARAM(nativeScreenPos.x, nativeScreenPos.y);
//const LPARAM clientPosLParam = MAKELPARAM(nativeClientPos.x, nativeClientPos.y);
if (((uMsg == WM_MOUSEMOVE) || (uMsg == WM_NCMOUSEMOVE)) && ((data.lastEventButtons & qtButtons) == 0)) {
switch (data.lastEventType) {
case WM_NCLBUTTONDBLCLK:
case WM_NCLBUTTONDOWN:
SendMessageW(hWnd, WM_NCLBUTTONUP, 0, screenPosLParam);
break;
case WM_NCMBUTTONDBLCLK:
case WM_NCMBUTTONDOWN:
SendMessageW(hWnd, WM_NCMBUTTONUP, 0, screenPosLParam);
break;
case WM_NCRBUTTONDBLCLK:
case WM_NCRBUTTONDOWN:
SendMessageW(hWnd, WM_NCRBUTTONUP, 0, screenPosLParam);
break;
default:
break;
const qreal devicePixelRatio = data.params.getWindowDevicePixelRatio();
const QPoint qtScenePos = QPointF(QPointF(qreal(nativeWindowPos.x), qreal(nativeWindowPos.y)) / devicePixelRatio).toPoint();
SystemButtonType currentButtonType = SystemButtonType::Unknown;
const ButtonState defaultButtonState = ButtonState::Unspecified;
data.params.setSystemButtonState(SystemButtonType::WindowIcon, defaultButtonState);
data.params.setSystemButtonState(SystemButtonType::Help, defaultButtonState);
data.params.setSystemButtonState(SystemButtonType::Minimize, defaultButtonState);
data.params.setSystemButtonState(SystemButtonType::Maximize, defaultButtonState);
data.params.setSystemButtonState(SystemButtonType::Restore, defaultButtonState);
data.params.setSystemButtonState(SystemButtonType::Close, defaultButtonState);
if (data.params.isInsideSystemButtons(qtScenePos, &currentButtonType)) {
Q_ASSERT(currentButtonType != SystemButtonType::Unknown);
if (currentButtonType != SystemButtonType::Unknown) {
const ButtonState currentButtonState = ((mouseButtons & Qt::LeftButton) ? ButtonState::Pressed : ButtonState::Hovered);
data.params.setSystemButtonState(currentButtonType, currentButtonState);
}
}
if (uMsg == WM_NCHITTEST) {
const qreal devicePixelRatio = data.params.getWindowDevicePixelRatio();
const QPoint qtScenePos = QPointF(QPointF(qreal(nativeClientPos.x),
qreal(nativeClientPos.y)) / devicePixelRatio).toPoint();
SystemButtonType systemButton = SystemButtonType::Unknown;
if (data.params.isInsideSystemButtons(qtScenePos, &systemButton)) {
const int hitTestResult = [systemButton]() -> int {
switch (systemButton) {
case SystemButtonType::WindowIcon:
return HTSYSMENU;
case SystemButtonType::Help:
return HTHELP;
case SystemButtonType::Minimize:
return HTREDUCE;
case SystemButtonType::Maximize:
case SystemButtonType::Restore:
return HTZOOM;
case SystemButtonType::Close:
return HTCLOSE;
case SystemButtonType::Unknown:
return HTCAPTION;
}
return 0;
}();
Q_ASSERT(hitTestResult);
if (hitTestResult != 0) {
return hitTestResult;
if ((uMsg == WM_NCHITTEST) && (currentButtonType != SystemButtonType::Unknown)) {
const int hitTestResult = [currentButtonType]() -> int {
switch (currentButtonType) {
case SystemButtonType::WindowIcon:
return HTSYSMENU;
case SystemButtonType::Help:
return HTHELP;
case SystemButtonType::Minimize:
return HTREDUCE;
case SystemButtonType::Maximize:
case SystemButtonType::Restore:
return HTZOOM;
case SystemButtonType::Close:
return HTCLOSE;
default:
break;
}
return 0;
}();
Q_ASSERT(hitTestResult);
if (hitTestResult != 0) {
return hitTestResult;
}
}
}
@ -235,7 +188,6 @@ FRAMELESSHELPER_STRING_CONSTANT(GetClientRect)
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
}
#endif
FramelessHelperWin::FramelessHelperWin() : QAbstractNativeEventFilter() {}
@ -281,27 +233,27 @@ void FramelessHelperWin::addWindow(const UserSettings &settings, const SystemPar
if (settings.options & Option::SyncNativeControlsThemeWithSystem) {
Utils::updateGlobalWin32ControlsTheme(params.windowId, dark);
}
if (Utils::isWin11OrGreater()) {
if (settings.options & Option::MaximizeButtonDocking) {
const auto hwnd = reinterpret_cast<HWND>(params.windowId);
SetLastError(ERROR_SUCCESS);
const auto originalWindowProc = reinterpret_cast<WNDPROC>(GetWindowLongPtrW(hwnd, GWLP_WNDPROC));
Q_ASSERT(originalWindowProc);
if (!originalWindowProc) {
qWarning() << Utils::getSystemErrorMessage(kGetWindowLongPtrW);
return;
}
g_win32Helper()->mutex.lock();
g_win32Helper()->data[params.windowId].originalWindowProc = originalWindowProc;
g_win32Helper()->mutex.unlock();
SetLastError(ERROR_SUCCESS);
if (SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(MaximizeDockingHookWindowProc)) == 0) {
qWarning() << Utils::getSystemErrorMessage(kSetWindowLongPtrW);
}
}
}
}
}
#if 0
if (settings.options & Option::MaximizeButtonDocking) {
const auto hwnd = reinterpret_cast<HWND>(params.windowId);
SetLastError(ERROR_SUCCESS);
const auto originalWindowProc = reinterpret_cast<WNDPROC>(GetWindowLongPtrW(hwnd, GWLP_WNDPROC));
Q_ASSERT(originalWindowProc);
if (!originalWindowProc) {
qWarning() << Utils::getSystemErrorMessage(kGetWindowLongPtrW);
return;
}
g_win32Helper()->mutex.lock();
g_win32Helper()->data[params.windowId].originalWindowProc = originalWindowProc;
g_win32Helper()->mutex.unlock();
SetLastError(ERROR_SUCCESS);
if (SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(MaximizeDockingHookWindowProc)) == 0) {
qWarning() << Utils::getSystemErrorMessage(kSetWindowLongPtrW);
}
}
#endif
}
bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result)

View File

@ -186,7 +186,7 @@ void Utils::moveWindowToDesktopCenter(const GetWindowScreenCallback &getWindowSc
return;
}
const QSize windowSize = getWindowSize();
if (windowSize.isEmpty() || (windowSize == kInvalidWindowSize)) {
if (windowSize.isEmpty() || (windowSize == kDefaultWindowSize)) {
return;
}
const QScreen *screen = getWindowScreen();
@ -230,7 +230,7 @@ bool Utils::isThemeChangeEvent(const QEvent * const event)
QColor Utils::calculateSystemButtonBackgroundColor(const SystemButtonType button, const ButtonState state)
{
if ((state == ButtonState::Unspecified) || (state == ButtonState::Released)) {
if (state == ButtonState::Unspecified) {
return kDefaultTransparentColor;
}
const QColor result = [button]() -> QColor {

View File

@ -60,7 +60,11 @@ endif()
target_compile_definitions(${SUB_PROJ_NAME} PRIVATE
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
QT_NO_URL_CAST_FROM_STRING
QT_NO_CAST_FROM_BYTEARRAY
QT_NO_KEYWORDS
QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
QT_NO_FOREACH
QT_USE_QSTRINGBUILDER
QT_DEPRECATED_WARNINGS
QT_DISABLE_DEPRECATED_BEFORE=0x060400

View File

@ -186,7 +186,7 @@ void FramelessQuickWindowPrivate::setFixedSize(const bool value, const bool forc
q->setFlags(q->flags() | Qt::MSWindowsFixedSizeDialogHint);
} else {
q->setFlags(q->flags() & ~Qt::MSWindowsFixedSizeDialogHint);
q->setMinimumSize(kInvalidWindowSize);
q->setMinimumSize(kDefaultWindowSize);
q->setMaximumSize(QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX));
}
#ifdef Q_OS_WINDOWS
@ -406,6 +406,13 @@ void FramelessQuickWindowPrivate::initialize()
m_params.isInsideSystemButtons = [this](const QPoint &pos, SystemButtonType *button) -> bool { return isInSystemButtons(pos, button); };
m_params.isInsideTitleBarDraggableArea = [this](const QPoint &pos) -> bool { return isInTitleBarDraggableArea(pos); };
m_params.getWindowDevicePixelRatio = [q]() -> qreal { return q->effectiveDevicePixelRatio(); };
m_params.setSystemButtonState = [q](const SystemButtonType button, const ButtonState state) -> void {
Q_ASSERT(button != SystemButtonType::Unknown);
if (button == SystemButtonType::Unknown) {
return;
}
Q_EMIT q->systemButtonStateChanged(button, state);
};
if (m_settings.options & Option::DisableResizing) {
setFixedSize(true, true);
}

View File

@ -58,7 +58,11 @@ endif()
target_compile_definitions(${SUB_PROJ_NAME} PRIVATE
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
QT_NO_URL_CAST_FROM_STRING
QT_NO_CAST_FROM_BYTEARRAY
QT_NO_KEYWORDS
QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
QT_NO_FOREACH
QT_USE_QSTRINGBUILDER
QT_DEPRECATED_WARNINGS
QT_DISABLE_DEPRECATED_BEFORE=0x060400

View File

@ -94,7 +94,7 @@ void FramelessWidgetsHelper::setFixedSize(const bool value, const bool force)
q->setWindowFlags(q->windowFlags() | Qt::MSWindowsFixedSizeDialogHint);
} else {
q->setWindowFlags(q->windowFlags() & ~Qt::MSWindowsFixedSizeDialogHint);
q->setMinimumSize(kInvalidWindowSize);
q->setMinimumSize(kDefaultWindowSize);
q->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
}
#ifdef Q_OS_WINDOWS
@ -366,6 +366,55 @@ void FramelessWidgetsHelper::initialize()
m_params.isInsideSystemButtons = [this](const QPoint &pos, SystemButtonType *button) -> bool { return isInSystemButtons(pos, button); };
m_params.isInsideTitleBarDraggableArea = [this](const QPoint &pos) -> bool { return isInTitleBarDraggableArea(pos); };
m_params.getWindowDevicePixelRatio = [this]() -> qreal { return q->devicePixelRatioF(); };
m_params.setSystemButtonState = [this](const SystemButtonType button, const ButtonState state) -> void {
Q_ASSERT(button != SystemButtonType::Unknown);
if (button == SystemButtonType::Unknown) {
return;
}
if (m_settings.options & Option::CreateStandardWindowLayout) {
const auto updateSystemButtonState = [state](StandardSystemButton *sysButton) -> void {
Q_ASSERT(sysButton);
if (!sysButton) {
return;
}
switch (state) {
case ButtonState::Unspecified: {
sysButton->setDown(false);
sysButton->setHover(false);
} break;
case ButtonState::Hovered: {
sysButton->setDown(false);
sysButton->setHover(true);
} break;
case ButtonState::Pressed: {
sysButton->setHover(true);
sysButton->setDown(true);
} break;
}
};
switch (button) {
case SystemButtonType::Minimize: {
if (m_systemMinimizeButton) {
updateSystemButtonState(m_systemMinimizeButton);
}
} break;
case SystemButtonType::Maximize:
case SystemButtonType::Restore: {
if (m_systemMaximizeButton) {
updateSystemButtonState(m_systemMaximizeButton);
}
} break;
case SystemButtonType::Close: {
if (m_systemCloseButton) {
updateSystemButtonState(m_systemCloseButton);
}
} break;
default:
break;
}
}
QMetaObject::invokeMethod(q, "systemButtonStateChanged", Q_ARG(Global::SystemButtonType, button), Q_ARG(Global::ButtonState, state));
};
if (m_settings.options & Option::CreateStandardWindowLayout) {
if (q->inherits(QT_MAINWINDOW_CLASS_NAME)) {
m_settings.options &= ~Options(Option::CreateStandardWindowLayout);