From fbe241f29ee18c317a54962590e7978341b7f99b Mon Sep 17 00:00:00 2001 From: Yuhang Zhao <2546789017@qq.com> Date: Fri, 29 Apr 2022 18:03:39 +0800 Subject: [PATCH] internal refactor Signed-off-by: Yuhang Zhao <2546789017@qq.com> --- examples/mainwindow/mainwindow.cpp | 1 + .../Core/framelesshelpercore_global.h | 16 +- .../Quick/framelesshelperquick_global.h | 1 + .../Quick/framelessquickutils.h | 14 + .../Widgets/standardsystembutton.h | 13 +- src/core/framelesshelper_qt.cpp | 93 +++-- src/core/framelesshelper_win.cpp | 178 +--------- src/quick/framelessquickutils.cpp | 50 +++ src/quick/framelessquickwindow.cpp | 232 ++++++------ src/quick/framelessquickwindow_p.h | 7 +- src/widgets/framelesswidgetshelper.cpp | 333 +++++++----------- src/widgets/framelesswidgetshelper_p.h | 10 +- src/widgets/standardsystembutton.cpp | 68 ++-- src/widgets/standardsystembutton_p.h | 6 +- 14 files changed, 435 insertions(+), 587 deletions(-) diff --git a/examples/mainwindow/mainwindow.cpp b/examples/mainwindow/mainwindow.cpp index a3d7773..83e69dc 100644 --- a/examples/mainwindow/mainwindow.cpp +++ b/examples/mainwindow/mainwindow.cpp @@ -71,6 +71,7 @@ void MainWindow::setupUi() setTitleBarWidget(titleBarWidget); + setHitTestVisible(mb); // IMPORTANT! setHitTestVisible(titleBar->iconButton); setHitTestVisible(titleBar->minimizeButton); setHitTestVisible(titleBar->maximizeButton); diff --git a/include/FramelessHelper/Core/framelesshelpercore_global.h b/include/FramelessHelper/Core/framelesshelpercore_global.h index df2b2bb..9e60b9d 100644 --- a/include/FramelessHelper/Core/framelesshelpercore_global.h +++ b/include/FramelessHelper/Core/framelesshelpercore_global.h @@ -277,7 +277,8 @@ enum class ButtonState { Unspecified = -1, Hovered = 0, - Pressed = 1 + Pressed = 1, + Clicked = 2 }; Q_ENUM_NS(ButtonState) @@ -404,6 +405,10 @@ using SetSystemButtonStateCallback = std::function; +using ShouldIgnoreMouseEventsCallback = std::function; + +using ShowSystemMenuCallback = std::function; + struct UserSettings { QPoint startupPosition = {}; @@ -451,6 +456,10 @@ struct SystemParameters GetWindowIdCallback getWindowId = nullptr; + ShouldIgnoreMouseEventsCallback shouldIgnoreMouseEvents = nullptr; + + ShowSystemMenuCallback showSystemMenu = nullptr; + [[nodiscard]] inline bool isValid() const { Q_ASSERT(getWindowFlags); @@ -472,13 +481,16 @@ struct SystemParameters Q_ASSERT(getWindowDevicePixelRatio); Q_ASSERT(setSystemButtonState); Q_ASSERT(getWindowId); + Q_ASSERT(shouldIgnoreMouseEvents); + Q_ASSERT(showSystemMenu); return (getWindowFlags && setWindowFlags && getWindowSize && setWindowSize && getWindowPosition && setWindowPosition && getWindowScreen && isWindowFixedSize && setWindowFixedSize && getWindowState && setWindowState && getWindowHandle && windowToScreen && screenToWindow && isInsideSystemButtons && isInsideTitleBarDraggableArea && getWindowDevicePixelRatio - && setSystemButtonState && getWindowId); + && setSystemButtonState && getWindowId && shouldIgnoreMouseEvents + && showSystemMenu); } }; diff --git a/include/FramelessHelper/Quick/framelesshelperquick_global.h b/include/FramelessHelper/Quick/framelesshelperquick_global.h index 89457dc..e274aa3 100644 --- a/include/FramelessHelper/Quick/framelesshelperquick_global.h +++ b/include/FramelessHelper/Quick/framelesshelperquick_global.h @@ -161,6 +161,7 @@ struct FRAMELESSHELPER_QUICK_API QuickGlobal FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, Unspecified) FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, Hovered) FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, Pressed) + FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, Clicked) }; Q_ENUM(ButtonState) diff --git a/include/FramelessHelper/Quick/framelessquickutils.h b/include/FramelessHelper/Quick/framelessquickutils.h index 9618ee9..80416c6 100644 --- a/include/FramelessHelper/Quick/framelessquickutils.h +++ b/include/FramelessHelper/Quick/framelessquickutils.h @@ -31,6 +31,7 @@ QT_BEGIN_NAMESPACE class QQuickWindow; +class QQuickItem; QT_END_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE @@ -80,6 +81,19 @@ public: Q_NODISCARD Q_INVOKABLE static QColor getSystemButtonBackgroundColor( const QuickGlobal::SystemButtonType button, const QuickGlobal::ButtonState state); +#if 0 +public Q_SLOTS: + static void removeWindowFrame(QQuickWindow *window); + static void setTitleBarItem(QQuickWindow *window, QQuickItem *item); + static void setHitTestVisible(QQuickWindow *window, QQuickItem *item); + static void setWindowFixedSize(QQuickWindow *window, const bool value); + static void moveWindowToDesktopCenter(QQuickWindow *window); + static void startSystemMove2(QQuickWindow *window, const QPoint &pos); + static void startSystemResize2(QQuickWindow *window, const Qt::Edges edges, const QPoint &pos); + static void bringWindowToFront(QQuickWindow *window); + static void showSystemMenu(QQuickWindow *window, const QPoint &pos); +#endif + Q_SIGNALS: void darkModeEnabledChanged(); void systemAccentColorChanged(); diff --git a/include/FramelessHelper/Widgets/standardsystembutton.h b/include/FramelessHelper/Widgets/standardsystembutton.h index 91baf76..a40cd89 100644 --- a/include/FramelessHelper/Widgets/standardsystembutton.h +++ b/include/FramelessHelper/Widgets/standardsystembutton.h @@ -37,7 +37,8 @@ class FRAMELESSHELPER_WIDGETS_API StandardSystemButton : public QAbstractButton Q_DECLARE_PRIVATE(StandardSystemButton) Q_DISABLE_COPY_MOVE(StandardSystemButton) Q_PROPERTY(Global::SystemButtonType buttonType READ buttonType WRITE setButtonType NOTIFY buttonTypeChanged FINAL) - Q_PROPERTY(bool hover READ isHover WRITE setHover NOTIFY hoverChanged FINAL) + Q_PROPERTY(bool hovered READ isHovered WRITE setHovered NOTIFY hoveredChanged FINAL) + Q_PROPERTY(bool pressed READ isPressed WRITE setPressed NOTIFY pressedChanged FINAL) Q_PROPERTY(QColor hoverColor READ hoverColor WRITE setHoverColor NOTIFY hoverColorChanged FINAL) Q_PROPERTY(QColor pressColor READ pressColor WRITE setPressColor NOTIFY pressColorChanged FINAL) @@ -53,8 +54,11 @@ public: Q_NODISCARD Global::SystemButtonType buttonType(); void setButtonType(const Global::SystemButtonType value); - Q_NODISCARD bool isHover() const; - void setHover(const bool value); + Q_NODISCARD bool isHovered() const; + void setHovered(const bool value); + + Q_NODISCARD bool isPressed() const; + void setPressed(const bool value); Q_NODISCARD QColor hoverColor() const; void setHoverColor(const QColor &value); @@ -69,7 +73,8 @@ protected: Q_SIGNALS: void buttonTypeChanged(); - void hoverChanged(); + void hoveredChanged(); + void pressedChanged(); void hoverColorChanged(); void pressColorChanged(); diff --git a/src/core/framelesshelper_qt.cpp b/src/core/framelesshelper_qt.cpp index 6c3e24d..e2d245b 100644 --- a/src/core/framelesshelper_qt.cpp +++ b/src/core/framelesshelper_qt.cpp @@ -40,6 +40,7 @@ struct QtHelperData SystemParameters params = {}; FramelessHelperQt *eventFilter = nullptr; bool cursorShapeChanged = false; + bool leftButtonPressed = false; }; struct QtHelper @@ -96,13 +97,14 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event) managerPriv->notifySystemThemeHasChangedOrNot(); return false; } - // Only monitor window events. + // We are only interested in events that are dispatched to top level windows. if (!object->isWindowType()) { return false; } const QEvent::Type type = event->type(); - // We are only interested in mouse events. - if ((type != QEvent::MouseButtonPress) && (type != QEvent::MouseMove)) { + // We are only interested in some specific mouse events. + if ((type != QEvent::MouseButtonPress) && (type != QEvent::MouseButtonRelease) + && (type != QEvent::MouseButtonDblClick) && (type != QEvent::MouseMove)) { return false; } const auto window = qobject_cast(object); @@ -114,10 +116,8 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event) } const QtHelperData data = g_qtHelper()->data.value(windowId); g_qtHelper()->mutex.unlock(); - if (data.params.isWindowFixedSize()) { - return false; - } const auto mouseEvent = static_cast(event); + const Qt::MouseButton button = mouseEvent->button(); #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) const QPoint scenePos = mouseEvent->scenePosition().toPoint(); const QPoint globalPos = mouseEvent->globalPosition().toPoint(); @@ -125,37 +125,68 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event) const QPoint scenePos = mouseEvent->windowPos().toPoint(); const QPoint globalPos = mouseEvent->screenPos().toPoint(); #endif + const bool windowFixedSize = data.params.isWindowFixedSize(); + const bool ignoreThisEvent = data.params.shouldIgnoreMouseEvents(scenePos); + const bool insideTitleBar = data.params.isInsideTitleBarDraggableArea(scenePos); switch (type) { - case QEvent::MouseMove: { - if (data.settings.options & Option::DontTouchCursorShape) { - return false; - } - const Qt::CursorShape cs = Utils::calculateCursorShape(window, scenePos); - if (cs == Qt::ArrowCursor) { - if (data.cursorShapeChanged) { - window->unsetCursor(); - QMutexLocker locker(&g_qtHelper()->mutex); - g_qtHelper()->data[windowId].cursorShapeChanged = false; - } else { - return false; + case QEvent::MouseButtonPress: { + if (button == Qt::LeftButton) { + g_qtHelper()->mutex.lock(); + g_qtHelper()->data[windowId].leftButtonPressed = true; + g_qtHelper()->mutex.unlock(); + if (!windowFixedSize) { + const Qt::Edges edges = Utils::calculateWindowEdges(window, scenePos); + if (edges != Qt::Edges{}) { + Utils::startSystemResize(window, edges, globalPos); + return true; + } } - } else { - window->setCursor(cs); - QMutexLocker locker(&g_qtHelper()->mutex); - g_qtHelper()->data[windowId].cursorShapeChanged = true; } } break; - case QEvent::MouseButtonPress: { - if (mouseEvent->button() != Qt::LeftButton) { - return false; + case QEvent::MouseButtonRelease: { + if (button == Qt::LeftButton) { + QMutexLocker locker(&g_qtHelper()->mutex); + g_qtHelper()->data[windowId].leftButtonPressed = false; } - const Qt::Edges edges = Utils::calculateWindowEdges(window, scenePos); - if (edges == Qt::Edges{}) { - return false; + if ((button == Qt::RightButton) && !(data.settings.options & Option::DisableSystemMenu)) { + if (!ignoreThisEvent && insideTitleBar) { + data.params.showSystemMenu(scenePos); + return true; + } } - Utils::startSystemResize(window, edges, globalPos); - return true; - } + } break; + case QEvent::MouseButtonDblClick: { + if ((button == Qt::LeftButton) && !windowFixedSize && !ignoreThisEvent + && insideTitleBar && !(data.settings.options & Option::NoDoubleClickMaximizeToggle)) { + Qt::WindowState newWindowState = Qt::WindowNoState; + if (data.params.getWindowState() != Qt::WindowMaximized) { + newWindowState = Qt::WindowMaximized; + } + data.params.setWindowState(newWindowState); + } + } break; + case QEvent::MouseMove: { + if (!windowFixedSize && !(data.settings.options & Option::DontTouchCursorShape)) { + const Qt::CursorShape cs = Utils::calculateCursorShape(window, scenePos); + if (cs == Qt::ArrowCursor) { + if (data.cursorShapeChanged) { + window->unsetCursor(); + QMutexLocker locker(&g_qtHelper()->mutex); + g_qtHelper()->data[windowId].cursorShapeChanged = false; + } + } else { + window->setCursor(cs); + QMutexLocker locker(&g_qtHelper()->mutex); + g_qtHelper()->data[windowId].cursorShapeChanged = true; + } + } + if (data.leftButtonPressed && !(data.settings.options & Option::DisableDragging)) { + if (!ignoreThisEvent && insideTitleBar) { + Utils::startSystemMove(window, globalPos); + return true; + } + } + } break; default: break; } diff --git a/src/core/framelesshelper_win.cpp b/src/core/framelesshelper_win.cpp index 614479a..6c73a67 100644 --- a/src/core/framelesshelper_win.cpp +++ b/src/core/framelesshelper_win.cpp @@ -42,7 +42,6 @@ struct Win32HelperData { UserSettings settings = {}; SystemParameters params = {}; - WNDPROC originalWindowProc = nullptr; }; struct Win32Helper @@ -73,142 +72,6 @@ FRAMELESSHELPER_STRING_CONSTANT(GetClientRect) FRAMELESSHELPER_STRING_CONSTANT2(SetWindowLongPtrW, "SetWindowLongW") #endif -[[nodiscard]] static inline Qt::MouseButtons keyStateToMouseButtons(const WPARAM wParam) -{ - if (wParam == 0) { - return {}; - } - Qt::MouseButtons result = {}; - if (wParam & MK_LBUTTON) { - result |= Qt::LeftButton; - } - if (wParam & MK_MBUTTON) { - result |= Qt::MiddleButton; - } - if (wParam & MK_RBUTTON) { - result |= Qt::RightButton; - } - if (wParam & MK_XBUTTON1) { - result |= Qt::XButton1; - } - if (wParam & MK_XBUTTON2) { - result |= Qt::XButton2; - } - return result; -} - -[[nodiscard]] static inline Qt::MouseButtons queryMouseButtons() -{ - Qt::MouseButtons result = {}; - const bool mouseSwapped = (GetSystemMetrics(SM_SWAPBUTTON) != FALSE); - if (GetAsyncKeyState(VK_LBUTTON) < 0) { - result |= (mouseSwapped ? Qt::RightButton: Qt::LeftButton); - } - if (GetAsyncKeyState(VK_MBUTTON) < 0) { - result |= Qt::MiddleButton; - } - if (GetAsyncKeyState(VK_RBUTTON) < 0) { - result |= (mouseSwapped ? Qt::LeftButton : Qt::RightButton); - } - if (GetAsyncKeyState(VK_XBUTTON1) < 0) { - result |= Qt::XButton1; - } - if (GetAsyncKeyState(VK_XBUTTON2) < 0) { - result |= Qt::XButton2; - } - return result; -} - -[[nodiscard]] static inline LRESULT CALLBACK MaximizeDockingHookWindowProc - (const HWND hWnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam) -{ - Q_ASSERT(hWnd); - if (!hWnd) { - return 0; - } - const auto windowId = reinterpret_cast(hWnd); - g_win32Helper()->mutex.lock(); - if (!g_win32Helper()->data.contains(windowId)) { - g_win32Helper()->mutex.unlock(); - return DefWindowProcW(hWnd, uMsg, wParam, lParam); - } - const Win32HelperData data = g_win32Helper()->data.value(windowId); - 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 mouseButtons = (isNonClientMouseEvent ? queryMouseButtons() : keyStateToMouseButtons(wParam)); - const POINT nativePosExtractedFromLParam = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; - POINT nativeScreenPos = {}; - POINT nativeWindowPos = {}; - if (isNonClientMouseEvent) { - nativeScreenPos = nativePosExtractedFromLParam; - nativeWindowPos = nativeScreenPos; - if (ScreenToClient(hWnd, &nativeWindowPos) == FALSE) { - qWarning() << Utils::getSystemErrorMessage(kScreenToClient); - return DefWindowProcW(hWnd, uMsg, wParam, lParam); - } - } - if (isClientMouseEvent) { - nativeWindowPos = nativePosExtractedFromLParam; - nativeScreenPos = nativeWindowPos; - if (ClientToScreen(hWnd, &nativeScreenPos) == FALSE) { - qWarning() << Utils::getSystemErrorMessage(kClientToScreen); - return DefWindowProcW(hWnd, uMsg, wParam, lParam); - } - } - const qreal devicePixelRatio = data.params.getWindowDevicePixelRatio(); - const QPoint qtScenePos = QPointF(QPointF(qreal(nativeWindowPos.x), qreal(nativeWindowPos.y)) / devicePixelRatio).toPoint(); - SystemButtonType currentButtonType = SystemButtonType::Unknown; - static constexpr const auto 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, ¤tButtonType)) { - 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) && (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; - } - } - } - Q_ASSERT(data.originalWindowProc); - if (data.originalWindowProc) { - return CallWindowProcW(data.originalWindowProc, hWnd, uMsg, wParam, lParam); - } else { - return DefWindowProcW(hWnd, uMsg, wParam, lParam); - } -} - FramelessHelperWin::FramelessHelperWin() : QAbstractNativeEventFilter() {} FramelessHelperWin::~FramelessHelperWin() = default; @@ -254,25 +117,6 @@ void FramelessHelperWin::addWindow(const UserSettings &settings, const SystemPar if (settings.options & Option::SyncNativeControlsThemeWithSystem) { Utils::updateGlobalWin32ControlsTheme(windowId, dark); } - if (Utils::isWindowsVersionOrGreater(WindowsVersion::_11_21H2)) { - if (settings.options & Option::MaximizeButtonDocking) { - const auto hwnd = reinterpret_cast(windowId); - SetLastError(ERROR_SUCCESS); - const auto originalWindowProc = reinterpret_cast(GetWindowLongPtrW(hwnd, GWLP_WNDPROC)); - Q_ASSERT(originalWindowProc); - if (!originalWindowProc) { - qWarning() << Utils::getSystemErrorMessage(kGetWindowLongPtrW); - return; - } - g_win32Helper()->mutex.lock(); - g_win32Helper()->data[windowId].originalWindowProc = originalWindowProc; - g_win32Helper()->mutex.unlock(); - SetLastError(ERROR_SUCCESS); - if (SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast(MaximizeDockingHookWindowProc)) == 0) { - qWarning() << Utils::getSystemErrorMessage(kSetWindowLongPtrW); - } - } - } } } } @@ -622,17 +466,23 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me *result = HTCLIENT; return true; } - const POINT globalPos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; - POINT localPos = globalPos; - if (ScreenToClient(hWnd, &localPos) == FALSE) { + const POINT nativeGlobalPos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; + POINT nativeLocalPos = nativeGlobalPos; + if (ScreenToClient(hWnd, &nativeLocalPos) == FALSE) { qWarning() << Utils::getSystemErrorMessage(kScreenToClient); break; } + const qreal dpr = data.params.getWindowDevicePixelRatio(); + const QPoint qtScenePos = QPointF(QPointF(qreal(nativeLocalPos.x), qreal(nativeLocalPos.y)) / dpr).toPoint(); const bool max = IsMaximized(hWnd); const bool full = Utils::isFullScreen(windowId); const int frameSizeY = Utils::getResizeBorderThickness(windowId, false, true); - const bool isTop = (localPos.y < frameSizeY); - const bool isTitleBar = (false && !(data.settings.options & Option::DisableDragging)); + const bool isTop = (nativeLocalPos.y < frameSizeY); + const bool buttonSwapped = (GetSystemMetrics(SM_SWAPBUTTON) != FALSE); + const bool leftButtonPressed = (buttonSwapped ? + (GetAsyncKeyState(VK_RBUTTON) < 0) : (GetAsyncKeyState(VK_LBUTTON) < 0)); + const bool isTitleBar = (data.params.isInsideTitleBarDraggableArea(qtScenePos) && + leftButtonPressed && !(data.settings.options & Option::DisableDragging)); if (frameBorderVisible) { // This will handle the left, right and bottom parts of the frame // because we didn't change them. @@ -680,13 +530,13 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me } const LONG width = clientRect.right; const LONG height = clientRect.bottom; - const bool isBottom = (localPos.y >= (height - frameSizeY)); + const bool isBottom = (nativeLocalPos.y >= (height - frameSizeY)); // Make the border a little wider to let the user easy to resize on corners. const qreal scaleFactor = ((isTop || isBottom) ? 2.0 : 1.0); const int frameSizeX = Utils::getResizeBorderThickness(windowId, true, true); const auto scaledFrameSizeX = static_cast(qRound(qreal(frameSizeX) * scaleFactor)); - const bool isLeft = (localPos.x < scaledFrameSizeX); - const bool isRight = (localPos.x >= (width - scaledFrameSizeX)); + const bool isLeft = (nativeLocalPos.x < scaledFrameSizeX); + const bool isRight = (nativeLocalPos.x >= (width - scaledFrameSizeX)); if (isTop) { if (isLeft) { *result = HTTOPLEFT; diff --git a/src/quick/framelessquickutils.cpp b/src/quick/framelessquickutils.cpp index f31bc25..3fb8f10 100644 --- a/src/quick/framelessquickutils.cpp +++ b/src/quick/framelessquickutils.cpp @@ -131,4 +131,54 @@ QColor FramelessQuickUtils::getSystemButtonBackgroundColor(const QuickGlobal::Sy FRAMELESSHELPER_ENUM_QUICK_TO_CORE(ButtonState, state)); } +#if 0 +void FramelessQuickUtils::removeWindowFrame(QQuickWindow *window) +{ + Q_ASSERT(window); + if (!window) { + return; + } +} + +void FramelessQuickUtils::setTitleBarItem(QQuickWindow *window, QQuickItem *item) +{ + +} + +void FramelessQuickUtils::setHitTestVisible(QQuickWindow *window, QQuickItem *item) +{ + +} + +void FramelessQuickUtils::setWindowFixedSize(QQuickWindow *window, const bool value) +{ + +} + +void FramelessQuickUtils::moveWindowToDesktopCenter(QQuickWindow *window) +{ + +} + +void FramelessQuickUtils::startSystemMove2(QQuickWindow *window, const QPoint &pos) +{ + +} + +void FramelessQuickUtils::startSystemResize2(QQuickWindow *window, const Qt::Edges edges, const QPoint &pos) +{ + +} + +void FramelessQuickUtils::bringWindowToFront(QQuickWindow *window) +{ + +} + +void FramelessQuickUtils::showSystemMenu(QQuickWindow *window, const QPoint &pos) +{ + +} +#endif + FRAMELESSHELPER_END_NAMESPACE diff --git a/src/quick/framelessquickwindow.cpp b/src/quick/framelessquickwindow.cpp index ef82940..619ac4c 100644 --- a/src/quick/framelessquickwindow.cpp +++ b/src/quick/framelessquickwindow.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -34,7 +35,8 @@ FRAMELESSHELPER_BEGIN_NAMESPACE using namespace Global; -static constexpr const char QT_QUICKITEM_CLASS_NAME[] = "QQuickItem"; +static constexpr const char QTQUICK_ITEM_CLASS_NAME[] = "QQuickItem"; +static constexpr const char QTQUICK_BUTTON_CLASS_NAME[] = "QQuickAbstractButton"; [[nodiscard]] static inline QuickGlobal::Options optionsCoreToQuick(const Options value) { @@ -370,22 +372,6 @@ bool FramelessQuickWindowPrivate::eventFilter(QObject *object, QEvent *event) const auto showEvent = static_cast(event); showEventHandler(showEvent); } break; - case QEvent::MouseMove: { - const auto mouseEvent = static_cast(event); - mouseMoveEventHandler(mouseEvent); - } break; - case QEvent::MouseButtonPress: { - const auto mouseEvent = static_cast(event); - mousePressEventHandler(mouseEvent); - } break; - case QEvent::MouseButtonRelease: { - const auto mouseEvent = static_cast(event); - mouseReleaseEventHandler(mouseEvent); - } break; - case QEvent::MouseButtonDblClick: { - const auto mouseEvent = static_cast(event); - mouseDoubleClickEventHandler(mouseEvent); - } break; default: break; } @@ -466,10 +452,6 @@ void FramelessQuickWindowPrivate::startSystemResize2(const Qt::Edges edges, cons void FramelessQuickWindowPrivate::initialize() { - if (m_initialized) { - return; - } - m_initialized = true; Q_Q(FramelessQuickWindow); m_params.getWindowId = [q]() -> WId { return q->winId(); }; m_params.getWindowFlags = [q]() -> Qt::WindowFlags { return q->flags(); }; @@ -494,14 +476,12 @@ void FramelessQuickWindowPrivate::initialize() }; 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(FRAMELESSHELPER_ENUM_CORE_TO_QUICK(SystemButtonType, button), - FRAMELESSHELPER_ENUM_CORE_TO_QUICK(ButtonState, state)); + m_params.setSystemButtonState = [this](const SystemButtonType button, const ButtonState state) -> void { + setSystemButtonState(FRAMELESSHELPER_ENUM_CORE_TO_QUICK(SystemButtonType, button), + FRAMELESSHELPER_ENUM_CORE_TO_QUICK(ButtonState, state)); }; + m_params.shouldIgnoreMouseEvents = [this](const QPoint &pos) -> bool { return shouldIgnoreMouseEvents(pos); }; + m_params.showSystemMenu = [this](const QPoint &pos) -> void { showSystemMenu(pos); }; if (m_settings.options & Option::DisableResizing) { setFixedSize(true, true); } @@ -563,39 +543,35 @@ bool FramelessQuickWindowPrivate::isInSystemButtons(const QPoint &pos, QuickGlob return false; } *button = QuickGlobal::SystemButtonType::Unknown; - if (!m_settings.windowIconButton && !m_settings.contextHelpButton - && !m_settings.minimizeButton && !m_settings.maximizeButton && !m_settings.closeButton) { - return false; - } - if (m_settings.windowIconButton && m_settings.windowIconButton->inherits(QT_QUICKITEM_CLASS_NAME)) { + if (m_settings.windowIconButton && m_settings.windowIconButton->inherits(QTQUICK_ITEM_CLASS_NAME)) { const auto iconBtn = qobject_cast(m_settings.windowIconButton); if (mapItemGeometryToScene(iconBtn).contains(pos)) { *button = QuickGlobal::SystemButtonType::WindowIcon; return true; } } - if (m_settings.contextHelpButton && m_settings.contextHelpButton->inherits(QT_QUICKITEM_CLASS_NAME)) { + if (m_settings.contextHelpButton && m_settings.contextHelpButton->inherits(QTQUICK_ITEM_CLASS_NAME)) { const auto helpBtn = qobject_cast(m_settings.contextHelpButton); if (mapItemGeometryToScene(helpBtn).contains(pos)) { *button = QuickGlobal::SystemButtonType::Help; return true; } } - if (m_settings.minimizeButton && m_settings.minimizeButton->inherits(QT_QUICKITEM_CLASS_NAME)) { + if (m_settings.minimizeButton && m_settings.minimizeButton->inherits(QTQUICK_ITEM_CLASS_NAME)) { const auto minBtn = qobject_cast(m_settings.minimizeButton); if (mapItemGeometryToScene(minBtn).contains(pos)) { *button = QuickGlobal::SystemButtonType::Minimize; return true; } } - if (m_settings.maximizeButton && m_settings.maximizeButton->inherits(QT_QUICKITEM_CLASS_NAME)) { + if (m_settings.maximizeButton && m_settings.maximizeButton->inherits(QTQUICK_ITEM_CLASS_NAME)) { const auto maxBtn = qobject_cast(m_settings.maximizeButton); if (mapItemGeometryToScene(maxBtn).contains(pos)) { *button = QuickGlobal::SystemButtonType::Maximize; return true; } } - if (m_settings.closeButton && m_settings.closeButton->inherits(QT_QUICKITEM_CLASS_NAME)) { + if (m_settings.closeButton && m_settings.closeButton->inherits(QTQUICK_ITEM_CLASS_NAME)) { const auto closeBtn = qobject_cast(m_settings.closeButton); if (mapItemGeometryToScene(closeBtn).contains(pos)) { *button = QuickGlobal::SystemButtonType::Close; @@ -611,6 +587,14 @@ bool FramelessQuickWindowPrivate::isInTitleBarDraggableArea(const QPoint &pos) c return false; } QRegion region = mapItemGeometryToScene(m_titleBarItem); + const auto systemButtons = {m_settings.windowIconButton, m_settings.contextHelpButton, + m_settings.minimizeButton, m_settings.maximizeButton, m_settings.closeButton}; + for (auto &&button : qAsConst(systemButtons)) { + if (button && button->inherits(QTQUICK_ITEM_CLASS_NAME)) { + const auto quickButton = qobject_cast(button); + region -= mapItemGeometryToScene(quickButton); + } + } if (!m_hitTestVisibleItems.isEmpty()) { for (auto &&item : qAsConst(m_hitTestVisibleItems)) { Q_ASSERT(item); @@ -650,6 +634,85 @@ bool FramelessQuickWindowPrivate::shouldDrawFrameBorder() const #endif } +void FramelessQuickWindowPrivate::setSystemButtonState(const QuickGlobal::SystemButtonType button, + const QuickGlobal::ButtonState state) +{ + Q_ASSERT(button != QuickGlobal::SystemButtonType::Unknown); + if (button == QuickGlobal::SystemButtonType::Unknown) { + return; + } + QQuickAbstractButton *quickButton = nullptr; + switch (button) { + case QuickGlobal::SystemButtonType::Unknown: { + Q_ASSERT(false); + } break; + case QuickGlobal::SystemButtonType::WindowIcon: { + if (m_settings.windowIconButton && m_settings.windowIconButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) { + quickButton = qobject_cast(m_settings.windowIconButton); + } + } break; + case QuickGlobal::SystemButtonType::Help: { + if (m_settings.contextHelpButton && m_settings.contextHelpButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) { + quickButton = qobject_cast(m_settings.contextHelpButton); + } + } break; + case QuickGlobal::SystemButtonType::Minimize: { + if (m_settings.minimizeButton && m_settings.minimizeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) { + quickButton = qobject_cast(m_settings.minimizeButton); + } + } break; + case QuickGlobal::SystemButtonType::Maximize: + case QuickGlobal::SystemButtonType::Restore: { + if (m_settings.maximizeButton && m_settings.maximizeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) { + quickButton = qobject_cast(m_settings.maximizeButton); + } + } break; + case QuickGlobal::SystemButtonType::Close: { + if (m_settings.closeButton && m_settings.closeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) { + quickButton = qobject_cast(m_settings.closeButton); + } + } break; + } + if (quickButton) { + const auto updateButtonState = [state](QQuickAbstractButton *btn) -> void { + Q_ASSERT(btn); + if (!btn) { + return; + } + switch (state) { + case QuickGlobal::ButtonState::Unspecified: { + btn->setDown(false); + btn->setPressed(false); + btn->setHovered(false); + } break; + case QuickGlobal::ButtonState::Hovered: { + btn->setDown(false); + btn->setPressed(false); + btn->setHovered(true); + } break; + case QuickGlobal::ButtonState::Pressed: { + btn->setHovered(true); + btn->setDown(true); + btn->setPressed(true); + } break; + case QuickGlobal::ButtonState::Clicked: { + // Clicked: pressed --> released, so behave like hovered. + btn->setDown(false); + btn->setPressed(false); + btn->setHovered(true); + // "QQuickAbstractButtonPrivate::click()"'s implementation is nothing but + // emits the "clicked" signal of the public interface, so we just emit + // the signal directly to avoid accessing the private implementation. + Q_EMIT btn->clicked(); + } break; + } + }; + updateButtonState(quickButton); + } + Q_Q(FramelessQuickWindow); + Q_EMIT q->systemButtonStateChanged(button, state); +} + void FramelessQuickWindowPrivate::showEventHandler(QShowEvent *event) { Q_ASSERT(event); @@ -675,101 +738,6 @@ void FramelessQuickWindowPrivate::showEventHandler(QShowEvent *event) } } -void FramelessQuickWindowPrivate::mouseMoveEventHandler(QMouseEvent *event) -{ - Q_ASSERT(event); - if (!event) { - return; - } - if (!m_mouseLeftButtonPressed) { - return; - } - if (m_settings.options & Option::DisableDragging) { - return; - } -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - const QPoint scenePos = event->scenePosition().toPoint(); - const QPoint globalPos = event->globalPosition().toPoint(); -#else - const QPoint scenePos = event->windowPos().toPoint(); - const QPoint globalPos = event->screenPos().toPoint(); -#endif - if (shouldIgnoreMouseEvents(scenePos)) { - return; - } - if (!isInTitleBarDraggableArea(scenePos)) { - return; - } - startSystemMove2(globalPos); -} - -void FramelessQuickWindowPrivate::mousePressEventHandler(QMouseEvent *event) -{ - Q_ASSERT(event); - if (!event) { - return; - } - if (event->button() == Qt::LeftButton) { - m_mouseLeftButtonPressed = true; - } -} - -void FramelessQuickWindowPrivate::mouseReleaseEventHandler(QMouseEvent *event) -{ - Q_ASSERT(event); - if (!event) { - return; - } - const Qt::MouseButton button = event->button(); - if (button == Qt::LeftButton) { - m_mouseLeftButtonPressed = false; - } - if (button != Qt::RightButton) { - return; - } - if (m_settings.options & Option::DisableSystemMenu) { - return; - } -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - const QPoint scenePos = event->scenePosition().toPoint(); -#else - const QPoint scenePos = event->windowPos().toPoint(); -#endif - if (shouldIgnoreMouseEvents(scenePos)) { - return; - } - if (!isInTitleBarDraggableArea(scenePos)) { - return; - } - showSystemMenu(scenePos); -} - -void FramelessQuickWindowPrivate::mouseDoubleClickEventHandler(QMouseEvent *event) -{ - Q_ASSERT(event); - if (!event) { - return; - } - if ((m_settings.options & Option::NoDoubleClickMaximizeToggle) || isFixedSize()) { - return; - } - if (event->button() != Qt::LeftButton) { - return; - } -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - const QPoint scenePos = event->scenePosition().toPoint(); -#else - const QPoint scenePos = event->windowPos().toPoint(); -#endif - if (shouldIgnoreMouseEvents(scenePos)) { - return; - } - if (!isInTitleBarDraggableArea(scenePos)) { - return; - } - toggleMaximized(); -} - QuickGlobal::Options FramelessQuickWindowPrivate::getOptions() const { return m_quickOptions; diff --git a/src/quick/framelessquickwindow_p.h b/src/quick/framelessquickwindow_p.h index e3daf07..2c20df6 100644 --- a/src/quick/framelessquickwindow_p.h +++ b/src/quick/framelessquickwindow_p.h @@ -66,10 +66,6 @@ public: Q_INVOKABLE Q_NODISCARD QQuickAnchorLine getTopBorderVerticalCenter() const; Q_INVOKABLE void showEventHandler(QShowEvent *event); - Q_INVOKABLE void mouseMoveEventHandler(QMouseEvent *event); - Q_INVOKABLE void mousePressEventHandler(QMouseEvent *event); - Q_INVOKABLE void mouseReleaseEventHandler(QMouseEvent *event); - Q_INVOKABLE void mouseDoubleClickEventHandler(QMouseEvent *event); Q_INVOKABLE Q_NODISCARD QuickGlobal::Options getOptions() const; @@ -100,6 +96,7 @@ private: Q_NODISCARD bool isInTitleBarDraggableArea(const QPoint &pos) const; Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const; Q_NODISCARD bool shouldDrawFrameBorder() const; + void setSystemButtonState(const QuickGlobal::SystemButtonType button, const QuickGlobal::ButtonState state); private Q_SLOTS: void updateTopBorderColor(); @@ -107,7 +104,6 @@ private Q_SLOTS: private: FramelessQuickWindow *q_ptr = nullptr; - bool m_initialized = false; QScopedPointer m_topBorderRectangle; QScopedPointer m_topBorderAnchors; QWindow::Visibility m_savedVisibility = QWindow::Windowed; @@ -117,7 +113,6 @@ private: QPointer m_titleBarItem = nullptr; QList m_hitTestVisibleItems = {}; QuickGlobal::Options m_quickOptions = {}; - bool m_mouseLeftButtonPressed = false; }; FRAMELESSHELPER_END_NAMESPACE diff --git a/src/widgets/framelesswidgetshelper.cpp b/src/widgets/framelesswidgetshelper.cpp index e5bb133..ca6149f 100644 --- a/src/widgets/framelesswidgetshelper.cpp +++ b/src/widgets/framelesswidgetshelper.cpp @@ -38,7 +38,8 @@ FRAMELESSHELPER_BEGIN_NAMESPACE using namespace Global; static constexpr const char FRAMELESSHELPER_PROP_NAME[] = "__wwx190_FramelessWidgetsHelper_instance"; -static constexpr const char QT_MAINWINDOW_CLASS_NAME[] = "QMainWindow"; +static constexpr const char QTWIDGETS_MAINWINDOW_CLASS_NAME[] = "QMainWindow"; +static constexpr const char FRAMELESSHELPER_SYSTEMBUTTON_CLASS_NAME[] = "StandardSystemButton"; FRAMELESSHELPER_STRING_CONSTANT2(StyleSheetColorTemplate, "color: %1;") FRAMELESSHELPER_STRING_CONSTANT2(StyleSheetBackgroundColorTemplate, "background-color: %1;") @@ -275,107 +276,8 @@ void FramelessWidgetsHelper::paintEventHandler(QPaintEvent *event) #endif } -void FramelessWidgetsHelper::mouseMoveEventHandler(QMouseEvent *event) -{ - Q_ASSERT(event); - if (!event) { - return; - } - if (!m_mouseLeftButtonPressed) { - return; - } - if (m_settings.options & Option::DisableDragging) { - return; - } -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - const QPoint scenePos = event->scenePosition().toPoint(); - const QPoint globalPos = event->globalPosition().toPoint(); -#else - const QPoint scenePos = event->windowPos().toPoint(); - const QPoint globalPos = event->screenPos().toPoint(); -#endif - if (shouldIgnoreMouseEvents(scenePos)) { - return; - } - if (!isInTitleBarDraggableArea(scenePos)) { - return; - } - startSystemMove2(globalPos); -} - -void FramelessWidgetsHelper::mousePressEventHandler(QMouseEvent *event) -{ - Q_ASSERT(event); - if (!event) { - return; - } - if (event->button() == Qt::LeftButton) { - m_mouseLeftButtonPressed = true; - } -} - -void FramelessWidgetsHelper::mouseReleaseEventHandler(QMouseEvent *event) -{ - Q_ASSERT(event); - if (!event) { - return; - } - const Qt::MouseButton button = event->button(); - if (button == Qt::LeftButton) { - m_mouseLeftButtonPressed = false; - } - if (button != Qt::RightButton) { - return; - } - if (m_settings.options & Option::DisableSystemMenu) { - return; - } -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - const QPoint scenePos = event->scenePosition().toPoint(); -#else - const QPoint scenePos = event->windowPos().toPoint(); -#endif - if (shouldIgnoreMouseEvents(scenePos)) { - return; - } - if (!isInTitleBarDraggableArea(scenePos)) { - return; - } - showSystemMenu(scenePos); -} - -void FramelessWidgetsHelper::mouseDoubleClickEventHandler(QMouseEvent *event) -{ - Q_ASSERT(event); - if (!event) { - return; - } - if ((m_settings.options & Option::NoDoubleClickMaximizeToggle) || isFixedSize()) { - return; - } - if (event->button() != Qt::LeftButton) { - return; - } -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - const QPoint scenePos = event->scenePosition().toPoint(); -#else - const QPoint scenePos = event->windowPos().toPoint(); -#endif - if (shouldIgnoreMouseEvents(scenePos)) { - return; - } - if (!isInTitleBarDraggableArea(scenePos)) { - return; - } - toggleMaximized(); -} - void FramelessWidgetsHelper::initialize() { - if (m_initialized) { - return; - } - m_initialized = true; // Let the user be able to get the helper class instance from outside. q->setProperty(FRAMELESSHELPER_PROP_NAME, QVariant::fromValue(this)); // Without this flag, Qt will always create an invisible native parent window @@ -410,57 +312,11 @@ 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.data()); - } - } break; - case SystemButtonType::Maximize: - case SystemButtonType::Restore: { - if (m_systemMaximizeButton) { - updateSystemButtonState(m_systemMaximizeButton.data()); - } - } break; - case SystemButtonType::Close: { - if (m_systemCloseButton) { - updateSystemButtonState(m_systemCloseButton.data()); - } - } break; - default: - break; - } - } - QMetaObject::invokeMethod(q, "systemButtonStateChanged", Q_ARG(Global::SystemButtonType, button), Q_ARG(Global::ButtonState, state)); - }; + m_params.setSystemButtonState = [this](const SystemButtonType button, const ButtonState state) -> void { setSystemButtonState(button, state); }; + m_params.shouldIgnoreMouseEvents = [this](const QPoint &pos) -> bool { return shouldIgnoreMouseEvents(pos); }; + m_params.showSystemMenu = [this](const QPoint &pos) -> void { showSystemMenu(pos); }; if (m_settings.options & Option::CreateStandardWindowLayout) { - if (q->inherits(QT_MAINWINDOW_CLASS_NAME)) { + if (q->inherits(QTWIDGETS_MAINWINDOW_CLASS_NAME)) { m_settings.options &= ~Options(Option::CreateStandardWindowLayout); qWarning() << "\"Option::CreateStandardWindowLayout\" is not compatible with QMainWindow and it's subclasses." " Enabling this option will mess up with your main window's layout."; @@ -495,11 +351,6 @@ void FramelessWidgetsHelper::initialize() QMetaObject::invokeMethod(q, "systemThemeChanged"); }); setupInitialUi(); - if (m_settings.options & Option::CreateStandardWindowLayout) { - m_settings.minimizeButton = m_systemMinimizeButton.data(); - m_settings.maximizeButton = m_systemMaximizeButton.data(); - m_settings.closeButton = m_systemCloseButton.data(); - } } void FramelessWidgetsHelper::createSystemTitleBar() @@ -517,30 +368,33 @@ void FramelessWidgetsHelper::createSystemTitleBar() m_systemWindowTitleLabel->setFont(windowTitleFont); m_systemWindowTitleLabel->setText(q->windowTitle()); connect(q, &QWidget::windowTitleChanged, m_systemWindowTitleLabel.data(), &QLabel::setText); - m_systemMinimizeButton.reset(new StandardSystemButton(SystemButtonType::Minimize, m_systemTitleBarWidget.data())); - m_systemMinimizeButton->setFixedSize(kDefaultSystemButtonSize); - m_systemMinimizeButton->setIconSize(kDefaultSystemButtonIconSize); - m_systemMinimizeButton->setToolTip(tr("Minimize")); - connect(m_systemMinimizeButton.data(), &StandardSystemButton::clicked, q, &QWidget::showMinimized); - m_systemMaximizeButton.reset(new StandardSystemButton(SystemButtonType::Maximize, m_systemTitleBarWidget.data())); - m_systemMaximizeButton->setFixedSize(kDefaultSystemButtonSize); - m_systemMaximizeButton->setIconSize(kDefaultSystemButtonIconSize); - connect(m_systemMaximizeButton.data(), &StandardSystemButton::clicked, this, &FramelessWidgetsHelper::toggleMaximized); - m_systemCloseButton.reset(new StandardSystemButton(SystemButtonType::Close, m_systemTitleBarWidget.data())); - m_systemCloseButton->setFixedSize(kDefaultSystemButtonSize); - m_systemCloseButton->setIconSize(kDefaultSystemButtonIconSize); - m_systemCloseButton->setToolTip(tr("Close")); - connect(m_systemCloseButton.data(), &StandardSystemButton::clicked, q, &QWidget::close); + const auto minBtn = new StandardSystemButton(SystemButtonType::Minimize, m_systemTitleBarWidget.data()); + minBtn->setFixedSize(kDefaultSystemButtonSize); + minBtn->setIconSize(kDefaultSystemButtonIconSize); + minBtn->setToolTip(tr("Minimize")); + connect(minBtn, &StandardSystemButton::clicked, q, &QWidget::showMinimized); + m_settings.minimizeButton = minBtn; + const auto maxBtn = new StandardSystemButton(SystemButtonType::Maximize, m_systemTitleBarWidget.data()); + maxBtn->setFixedSize(kDefaultSystemButtonSize); + maxBtn->setIconSize(kDefaultSystemButtonIconSize); updateSystemMaximizeButton(); + connect(maxBtn, &StandardSystemButton::clicked, this, &FramelessWidgetsHelper::toggleMaximized); + m_settings.maximizeButton = maxBtn; + const auto closeBtn = new StandardSystemButton(SystemButtonType::Close, m_systemTitleBarWidget.data()); + closeBtn->setFixedSize(kDefaultSystemButtonSize); + closeBtn->setIconSize(kDefaultSystemButtonIconSize); + closeBtn->setToolTip(tr("Close")); + connect(closeBtn, &StandardSystemButton::clicked, q, &QWidget::close); + m_settings.closeButton = closeBtn; const auto systemTitleBarLayout = new QHBoxLayout(m_systemTitleBarWidget.data()); systemTitleBarLayout->setContentsMargins(0, 0, 0, 0); systemTitleBarLayout->setSpacing(0); systemTitleBarLayout->addSpacerItem(new QSpacerItem(kDefaultTitleBarTitleLabelMargin, kDefaultTitleBarTitleLabelMargin)); systemTitleBarLayout->addWidget(m_systemWindowTitleLabel.data()); systemTitleBarLayout->addStretch(); - systemTitleBarLayout->addWidget(m_systemMinimizeButton.data()); - systemTitleBarLayout->addWidget(m_systemMaximizeButton.data()); - systemTitleBarLayout->addWidget(m_systemCloseButton.data()); + systemTitleBarLayout->addWidget(minBtn); + systemTitleBarLayout->addWidget(maxBtn); + systemTitleBarLayout->addWidget(closeBtn); m_systemTitleBarWidget->setLayout(systemTitleBarLayout); } @@ -632,29 +486,25 @@ bool FramelessWidgetsHelper::isInSystemButtons(const QPoint &pos, SystemButtonTy bool FramelessWidgetsHelper::isInTitleBarDraggableArea(const QPoint &pos) const { - const QRegion draggableRegion = [this]() -> QRegion { - if (m_userTitleBarWidget) { - QRegion region = mapWidgetGeometryToScene(m_userTitleBarWidget); - if (!m_hitTestVisibleWidgets.isEmpty()) { - for (auto &&widget : qAsConst(m_hitTestVisibleWidgets)) { - Q_ASSERT(widget); - if (widget) { - region -= mapWidgetGeometryToScene(widget); - } - } + QRegion region = (m_userTitleBarWidget ? mapWidgetGeometryToScene(m_userTitleBarWidget) : + (m_systemTitleBarWidget ? mapWidgetGeometryToScene(m_systemTitleBarWidget.data()) : QRegion{})); + const auto systemButtons = {m_settings.windowIconButton, m_settings.contextHelpButton, + m_settings.minimizeButton, m_settings.maximizeButton, m_settings.closeButton}; + for (auto &&button : qAsConst(systemButtons)) { + if (button && button->isWidgetType()) { + const auto widgetButton = qobject_cast(button); + region -= mapWidgetGeometryToScene(widgetButton); + } + } + if (!m_hitTestVisibleWidgets.isEmpty()) { + for (auto &&widget : qAsConst(m_hitTestVisibleWidgets)) { + Q_ASSERT(widget); + if (widget) { + region -= mapWidgetGeometryToScene(widget); } - return region; } - if (m_settings.options & Option::CreateStandardWindowLayout) { - QRegion region = mapWidgetGeometryToScene(m_systemTitleBarWidget.data()); - region -= mapWidgetGeometryToScene(m_systemMinimizeButton.data()); - region -= mapWidgetGeometryToScene(m_systemMaximizeButton.data()); - region -= mapWidgetGeometryToScene(m_systemCloseButton.data()); - return region; - } - return {}; - }(); - return draggableRegion.contains(pos); + } + return region.contains(pos); } bool FramelessWidgetsHelper::shouldDrawFrameBorder() const @@ -684,6 +534,78 @@ bool FramelessWidgetsHelper::shouldIgnoreMouseEvents(const QPoint &pos) const return (isNormal() && withinFrameBorder); } +void FramelessWidgetsHelper::setSystemButtonState(const SystemButtonType button, const ButtonState state) +{ + Q_ASSERT(button != SystemButtonType::Unknown); + if (button == SystemButtonType::Unknown) { + return; + } + QWidget *widgetButton = nullptr; + switch (button) { + case SystemButtonType::Unknown: { + Q_ASSERT(false); + } break; + case SystemButtonType::WindowIcon: { + if (m_settings.windowIconButton && m_settings.windowIconButton->isWidgetType()) { + widgetButton = qobject_cast(m_settings.windowIconButton); + } + } break; + case SystemButtonType::Help: { + if (m_settings.contextHelpButton && m_settings.contextHelpButton->isWidgetType()) { + widgetButton = qobject_cast(m_settings.contextHelpButton); + } + } break; + case SystemButtonType::Minimize: { + if (m_settings.minimizeButton && m_settings.minimizeButton->isWidgetType()) { + widgetButton = qobject_cast(m_settings.minimizeButton); + } + } break; + case SystemButtonType::Maximize: + case SystemButtonType::Restore: { + if (m_settings.maximizeButton && m_settings.maximizeButton->isWidgetType()) { + widgetButton = qobject_cast(m_settings.maximizeButton); + } + } break; + case SystemButtonType::Close: { + if (m_settings.closeButton && m_settings.closeButton->isWidgetType()) { + widgetButton = qobject_cast(m_settings.closeButton); + } + } break; + } + if (widgetButton) { + const auto updateButtonState = [state](QWidget *btn) -> void { + Q_ASSERT(btn); + if (!btn) { + return; + } + switch (state) { + case ButtonState::Unspecified: { + QMetaObject::invokeMethod(btn, "setPressed", Q_ARG(bool, false)); + QMetaObject::invokeMethod(btn, "setHovered", Q_ARG(bool, false)); + } break; + case ButtonState::Hovered: { + QMetaObject::invokeMethod(btn, "setPressed", Q_ARG(bool, false)); + QMetaObject::invokeMethod(btn, "setHovered", Q_ARG(bool, true)); + } break; + case ButtonState::Pressed: { + QMetaObject::invokeMethod(btn, "setHovered", Q_ARG(bool, true)); + QMetaObject::invokeMethod(btn, "setPressed", Q_ARG(bool, true)); + } break; + case ButtonState::Clicked: { + // Clicked: pressed --> released, so behave like hovered. + QMetaObject::invokeMethod(btn, "setPressed", Q_ARG(bool, false)); + QMetaObject::invokeMethod(btn, "setHovered", Q_ARG(bool, true)); + // Trigger the clicked signal. + QMetaObject::invokeMethod(btn, "clicked"); + } break; + } + }; + updateButtonState(widgetButton); + } + QMetaObject::invokeMethod(q, "systemButtonStateChanged", + Q_ARG(Global::SystemButtonType, button), Q_ARG(Global::ButtonState, state)); +} + void FramelessWidgetsHelper::updateContentsMargins() { #ifdef Q_OS_WINDOWS @@ -733,12 +655,11 @@ void FramelessWidgetsHelper::updateSystemTitleBarStyleSheet() void FramelessWidgetsHelper::updateSystemMaximizeButton() { - if (!(m_settings.options & Option::CreateStandardWindowLayout)) { - return; + if (const auto button = qobject_cast(m_settings.maximizeButton)) { + const bool zoomed = isZoomed(); + button->setToolTip(zoomed ? tr("Restore") : tr("Maximize")); + button->setButtonType(zoomed ? SystemButtonType::Restore : SystemButtonType::Maximize); } - const bool zoomed = isZoomed(); - m_systemMaximizeButton->setToolTip(zoomed ? tr("Restore") : tr("Maximize")); - m_systemMaximizeButton->setButtonType(zoomed ? SystemButtonType::Restore : SystemButtonType::Maximize); } void FramelessWidgetsHelper::toggleMaximized() @@ -830,22 +751,6 @@ bool FramelessWidgetsHelper::eventFilter(QObject *object, QEvent *event) const auto paintEvent = static_cast(event); paintEventHandler(paintEvent); } break; - case QEvent::MouseMove: { - const auto mouseEvent = static_cast(event); - mouseMoveEventHandler(mouseEvent); - } break; - case QEvent::MouseButtonPress: { - const auto mouseEvent = static_cast(event); - mousePressEventHandler(mouseEvent); - } break; - case QEvent::MouseButtonRelease: { - const auto mouseEvent = static_cast(event); - mouseReleaseEventHandler(mouseEvent); - } break; - case QEvent::MouseButtonDblClick: { - const auto mouseEvent = static_cast(event); - mouseDoubleClickEventHandler(mouseEvent); - } break; case QEvent::ActivationChange: case QEvent::WindowStateChange: { changeEventHandler(event); diff --git a/src/widgets/framelesswidgetshelper_p.h b/src/widgets/framelesswidgetshelper_p.h index a526978..d863f22 100644 --- a/src/widgets/framelesswidgetshelper_p.h +++ b/src/widgets/framelesswidgetshelper_p.h @@ -66,10 +66,6 @@ public: Q_INVOKABLE void showEventHandler(QShowEvent *event); Q_INVOKABLE void changeEventHandler(QEvent *event); Q_INVOKABLE void paintEventHandler(QPaintEvent *event); - Q_INVOKABLE void mouseMoveEventHandler(QMouseEvent *event); - Q_INVOKABLE void mousePressEventHandler(QMouseEvent *event); - Q_INVOKABLE void mouseReleaseEventHandler(QMouseEvent *event); - Q_INVOKABLE void mouseDoubleClickEventHandler(QMouseEvent *event); public Q_SLOTS: void setHitTestVisible(QWidget *widget); @@ -94,6 +90,7 @@ private: Q_NODISCARD bool isInTitleBarDraggableArea(const QPoint &pos) const; Q_NODISCARD bool shouldDrawFrameBorder() const; Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const; + void setSystemButtonState(const Global::SystemButtonType button, const Global::ButtonState state); private Q_SLOTS: void updateContentsMargins(); @@ -102,12 +99,8 @@ private Q_SLOTS: private: QPointer q = nullptr; - bool m_initialized = false; QScopedPointer m_systemTitleBarWidget; QScopedPointer m_systemWindowTitleLabel; - QScopedPointer m_systemMinimizeButton; - QScopedPointer m_systemMaximizeButton; - QScopedPointer m_systemCloseButton; QPointer m_userTitleBarWidget = nullptr; QPointer m_userContentWidget = nullptr; QScopedPointer m_mainLayout; @@ -118,7 +111,6 @@ private: Global::UserSettings m_settings = {}; Global::SystemParameters m_params = {}; bool m_windowExposed = false; - bool m_mouseLeftButtonPressed = false; }; FRAMELESSHELPER_END_NAMESPACE diff --git a/src/widgets/standardsystembutton.cpp b/src/widgets/standardsystembutton.cpp index 1047a6f..9adfbb5 100644 --- a/src/widgets/standardsystembutton.cpp +++ b/src/widgets/standardsystembutton.cpp @@ -172,11 +172,16 @@ QSize StandardSystemButtonPrivate::getRecommendedButtonSize() const return kDefaultSystemButtonSize; } -bool StandardSystemButtonPrivate::isHover() const +bool StandardSystemButtonPrivate::isHovered() const { return m_hovered; } +bool StandardSystemButtonPrivate::isPressed() const +{ + return m_pressed; +} + QColor StandardSystemButtonPrivate::getHoverColor() const { return m_hoverColor; @@ -187,7 +192,7 @@ QColor StandardSystemButtonPrivate::getPressColor() const return m_pressColor; } -void StandardSystemButtonPrivate::setHover(const bool value) +void StandardSystemButtonPrivate::setHovered(const bool value) { if (m_hovered == value) { return; @@ -195,7 +200,24 @@ void StandardSystemButtonPrivate::setHover(const bool value) m_hovered = value; Q_Q(StandardSystemButton); q->update(); - Q_EMIT q->hoverChanged(); + Q_EMIT q->hoveredChanged(); +} + +void StandardSystemButtonPrivate::setPressed(const bool value) +{ + if (m_pressed == value) { + return; + } + m_pressed = value; + Q_Q(StandardSystemButton); + q->setDown(m_pressed); + q->update(); + Q_EMIT q->pressedChanged(); + if (m_pressed) { + Q_EMIT q->pressed(); + } else { + Q_EMIT q->released(); + } } void StandardSystemButtonPrivate::setHoverColor(const QColor &value) @@ -234,7 +256,7 @@ void StandardSystemButtonPrivate::enterEventHandler(QT_ENTER_EVENT_TYPE *event) if (!event) { return; } - setHover(true); + setHovered(true); } void StandardSystemButtonPrivate::leaveEventHandler(QEvent *event) @@ -243,7 +265,7 @@ void StandardSystemButtonPrivate::leaveEventHandler(QEvent *event) if (!event) { return; } - setHover(false); + setHovered(false); } void StandardSystemButtonPrivate::paintEventHandler(QPaintEvent *event) @@ -282,20 +304,8 @@ void StandardSystemButtonPrivate::initialize() Q_Q(StandardSystemButton); q->setFixedSize(kDefaultSystemButtonSize); q->setIconSize(kDefaultSystemButtonIconSize); - connect(q, &StandardSystemButton::pressed, this, [this, q](){ - if (m_pressed) { - return; - } - m_pressed = true; - q->update(); - }); - connect(q, &StandardSystemButton::released, this, [this, q](){ - if (!m_pressed) { - return; - } - m_pressed = false; - q->update(); - }); + connect(q, &StandardSystemButton::pressed, this, [this](){ setPressed(true); }); + connect(q, &StandardSystemButton::released, this, [this](){ setPressed(false); }); connect(FramelessWindowsManager::instance(), &FramelessWindowsManager::systemThemeChanged, this, [this](){ refreshButtonTheme(false); }); } @@ -336,16 +346,28 @@ void StandardSystemButton::setButtonType(const Global::SystemButtonType value) d->setButtonType(value); } -bool StandardSystemButton::isHover() const +bool StandardSystemButton::isHovered() const { Q_D(const StandardSystemButton); - return d->isHover(); + return d->isHovered(); } -void StandardSystemButton::setHover(const bool value) +void StandardSystemButton::setHovered(const bool value) { Q_D(StandardSystemButton); - d->setHover(value); + d->setHovered(value); +} + +bool StandardSystemButton::isPressed() const +{ + Q_D(const StandardSystemButton); + return d->isPressed(); +} + +void StandardSystemButton::setPressed(const bool value) +{ + Q_D(StandardSystemButton); + d->setPressed(value); } QColor StandardSystemButton::hoverColor() const diff --git a/src/widgets/standardsystembutton_p.h b/src/widgets/standardsystembutton_p.h index b4724de..0bb948a 100644 --- a/src/widgets/standardsystembutton_p.h +++ b/src/widgets/standardsystembutton_p.h @@ -61,11 +61,13 @@ public: Q_NODISCARD QSize getRecommendedButtonSize() const; - Q_NODISCARD bool isHover() const; + Q_NODISCARD bool isHovered() const; + Q_NODISCARD bool isPressed() const; Q_NODISCARD QColor getHoverColor() const; Q_NODISCARD QColor getPressColor() const; - void setHover(const bool value); + void setHovered(const bool value); + void setPressed(const bool value); void setHoverColor(const QColor &value); void setPressColor(const QColor &value);