From 12988f2ddfceb2d78bfcc678f67fbb27638c5a9f Mon Sep 17 00:00:00 2001 From: Yuhang Zhao <2546789017@qq.com> Date: Sun, 20 Mar 2022 15:51:05 +0800 Subject: [PATCH] add the Options feature to control some details Signed-off-by: Yuhang Zhao <2546789017@qq.com> --- examples/mainwindow/mainwindow.cpp | 2 +- examples/mainwindow/mainwindow.h | 2 +- examples/widget/widget.cpp | 2 +- src/core/framelesshelper_win.cpp | 44 ++++++++++--- src/core/framelesshelpercore_global.h | 26 ++++++-- src/core/framelesswindowsmanager.cpp | 11 +++- src/core/utils.h | 2 + src/core/utils_win.cpp | 47 +++++++++++--- src/quick/framelessquickeventfilter.cpp | 7 ++ src/widgets/framelessmainwindow.cpp | 10 +-- src/widgets/framelessmainwindow.h | 3 +- src/widgets/framelesswidget.cpp | 10 +-- src/widgets/framelesswidget.h | 3 +- src/widgets/framelesswidgetshelper.cpp | 86 +++++++++++++------------ src/widgets/framelesswidgetshelper.h | 9 +-- 15 files changed, 170 insertions(+), 94 deletions(-) diff --git a/examples/mainwindow/mainwindow.cpp b/examples/mainwindow/mainwindow.cpp index 74d1d8d..f2628dc 100644 --- a/examples/mainwindow/mainwindow.cpp +++ b/examples/mainwindow/mainwindow.cpp @@ -29,7 +29,7 @@ FRAMELESSHELPER_USE_NAMESPACE -MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) : FramelessMainWindow(parent, flags) +MainWindow::MainWindow(QWidget *parent, const Qt::WindowFlags flags) : FramelessMainWindow(parent, flags) { setupUi(); } diff --git a/examples/mainwindow/mainwindow.h b/examples/mainwindow/mainwindow.h index 878e9ba..fc8e21c 100644 --- a/examples/mainwindow/mainwindow.h +++ b/examples/mainwindow/mainwindow.h @@ -38,7 +38,7 @@ class MainWindow : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessMainWindow) Q_DISABLE_COPY_MOVE(MainWindow) public: - explicit MainWindow(QWidget *parent = nullptr, Qt::WindowFlags flags = {}); + explicit MainWindow(QWidget *parent = nullptr, const Qt::WindowFlags flags = {}); ~MainWindow() override; protected: diff --git a/examples/widget/widget.cpp b/examples/widget/widget.cpp index 20224b9..ab2ea72 100644 --- a/examples/widget/widget.cpp +++ b/examples/widget/widget.cpp @@ -30,7 +30,7 @@ FRAMELESSHELPER_USE_NAMESPACE -Widget::Widget(QWidget *parent) : FramelessWidget(parent, WindowLayout::Standard) +Widget::Widget(QWidget *parent) : FramelessWidget(parent, {Option::UseStandardWindowLayout}) { setupUi(); startTimer(500); diff --git a/src/core/framelesshelper_win.cpp b/src/core/framelesshelper_win.cpp index 0214133..0502da5 100644 --- a/src/core/framelesshelper_win.cpp +++ b/src/core/framelesshelper_win.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include "framelesswindowsmanager.h" @@ -76,11 +77,16 @@ void FramelessHelperWin::addWindow(QWindow *window) qApp->installNativeEventFilter(g_win32Helper()->nativeEventFilter.data()); } g_win32Helper()->mutex.unlock(); - Utils::fixupQtInternals(winId); + const auto options = qvariant_cast(window->property(kInternalOptionsFlag)); + if (!(options & Option::DontModifyQtInternals)) { + Utils::fixupQtInternals(winId); + } Utils::updateInternalWindowFrameMargins(window, true); Utils::updateWindowFrameMargins(winId, false); - const bool dark = Utils::shouldAppsUseDarkMode(); - Utils::updateWindowFrameBorderColor(winId, dark); + if (!(options & Option::DontModifyWindowFrameBorderColor)) { + const bool dark = Utils::shouldAppsUseDarkMode(); + Utils::updateWindowFrameBorderColor(winId, dark); + } } void FramelessHelperWin::removeWindow(QWindow *window) @@ -119,7 +125,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me #endif if (!msg->hwnd) { // Why sometimes the window handle is null? Is it designed to be like this? - // Anyway, we should skip the entire function in this case. + // Anyway, we should skip the entire processing in this case. return false; } const WId winId = reinterpret_cast(msg->hwnd); @@ -134,6 +140,22 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me if (!window) { return false; } + auto options = qvariant_cast(window->property(kInternalOptionsFlag)); + if ((options & Option::ForceHideWindowFrameBorder) && (options & Option::ForceShowWindowFrameBorder)) { + qWarning() << "You can't use both \"Option::ForceHideWindowFrameBorder\" and " + "\"Option::ForceShowWindowFrameBorder\" at the same time."; + options &= ~(Option::ForceHideWindowFrameBorder | Option::ForceShowWindowFrameBorder); + window->setProperty(kInternalOptionsFlag, QVariant::fromValue(options)); + } + const bool frameBorderVisible = [options]() -> bool { + if (options & Option::ForceShowWindowFrameBorder) { + return true; + } + if (options & Option::ForceHideWindowFrameBorder) { + return false; + } + return Utils::isWindowFrameBorderVisible(); + }(); switch (msg->message) { case WM_NCCALCSIZE: { // Windows是根据这个消息的返回值来设置窗口的客户区(窗口中真正显示的内容) @@ -226,7 +248,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me const auto clientRect = ((static_cast(msg->wParam) == FALSE) ? reinterpret_cast(msg->lParam) : &(reinterpret_cast(msg->lParam))->rgrc[0]); - if (Utils::isWindowFrameBorderVisible()) { + if (frameBorderVisible) { // Store the original top before the default window proc applies the default frame. const LONG originalTop = clientRect->top; // Apply the default frame. @@ -252,7 +274,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me // a window when it's maximized unless you restore it). const int frameSizeY = Utils::getResizeBorderThickness(winId, false, true); clientRect->top += frameSizeY; - if (!Utils::isWindowFrameBorderVisible()) { + if (!frameBorderVisible) { clientRect->bottom -= frameSizeY; const int frameSizeX = Utils::getResizeBorderThickness(winId, true, true); clientRect->left += frameSizeX; @@ -466,7 +488,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me const int frameSizeY = Utils::getResizeBorderThickness(winId, false, true); const bool isTop = (localPos.y < frameSizeY); static constexpr const bool isTitleBar = false; - if (Utils::isWindowFrameBorderVisible()) { + if (frameBorderVisible) { // This will handle the left, right and bottom parts of the frame // because we didn't change them. const LRESULT originalRet = DefWindowProcW(msg->hwnd, WM_NCHITTEST, 0, msg->lParam); @@ -579,7 +601,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me default: break; } - if (!Utils::isWindowFrameBorderVisible()) { + if (!frameBorderVisible) { switch (msg->message) { case WM_NCUAHDRAWCAPTION: case WM_NCUAHDRAWFRAME: { @@ -665,8 +687,10 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me return false; }(); if (themeSettingChanged) { - const bool dark = Utils::shouldAppsUseDarkMode(); - Utils::updateWindowFrameBorderColor(winId, dark); + if (!(options & Option::DontModifyWindowFrameBorderColor)) { + const bool dark = Utils::shouldAppsUseDarkMode(); + Utils::updateWindowFrameBorderColor(winId, dark); + } } if (themeSettingChanged || (msg->message == WM_THEMECHANGED) || (msg->message == WM_DWMCOLORIZATIONCOLORCHANGED)) { diff --git a/src/core/framelesshelpercore_global.h b/src/core/framelesshelpercore_global.h index 9c0392c..a44c3b7 100644 --- a/src/core/framelesshelpercore_global.h +++ b/src/core/framelesshelpercore_global.h @@ -108,11 +108,29 @@ 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}; -enum class WindowLayout : int +[[maybe_unused]] static constexpr const char kInternalOptionsFlag[] = "FRAMELESSHELPER_INTERNAL_OPTIONS"; + +enum class Option : int { - Standard = 0, - Custom = 1 + Default = 0x00000000, // Default placeholder, have no effect. + ForceHideWindowFrameBorder = 0x00000001, // Windows only, force hide the window frame border even on Windows 10 and onwards. + ForceShowWindowFrameBorder = 0x00000002, // Windows only, force show the window frame border even on Windows 7 (~ 8.1). + DontDrawTopWindowFrameBorder = 0x00000004, // Windows only, don't draw the top window frame border even if the window frame border is visible. + EnableRoundedWindowCorners = 0x00000008, // Not implemented yet. + TransparentWindowBackground = 0x00000010, // Not implemented yet. + MaximizeButtonDocking = 0x00000020, // Windows only, enable the window docking feature introduced in Windows 11. + UseStandardWindowLayout = 0x00000040, // The standard window layout is a titlebar always on top and fill the window width. + BeCompatibleWithQtFramelessWindowHint = 0x00000080, // Windows only, make the code compatible with Qt::FramelessWindowHint. Don't use this option unless you really need that flag. + DontModifyQtInternals = 0x00000100, // Windows only, don't touch Qt's internal data. + DontModifyWindowFrameBorderColor = 0x00000200, // Windows only, don't touch the window frame border color. + DontInstallSystemMenuHook = 0x00000400, // Windows only, don't install the system menu hook. + DisableSystemMenu = 0x00000800, // Windows only, don't open the system menu when right clicks the titlebar. + NoDoubleClickMaximizeToggle = 0x00001000 // Don't toggle the maximize state when double clicks the titlebar. }; -Q_ENUM_NS(WindowLayout) +Q_FLAG_NS(Option) +Q_DECLARE_FLAGS(Options, Option) +Q_DECLARE_OPERATORS_FOR_FLAGS(Options) FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Options)) diff --git a/src/core/framelesswindowsmanager.cpp b/src/core/framelesswindowsmanager.cpp index 6f15c27..5c4bdfa 100644 --- a/src/core/framelesswindowsmanager.cpp +++ b/src/core/framelesswindowsmanager.cpp @@ -24,6 +24,7 @@ #include "framelesswindowsmanager.h" #include "framelesswindowsmanager_p.h" +#include #include #include #include "framelesshelper_qt.h" @@ -182,7 +183,10 @@ void FramelessWindowsManager::addWindow(QWindow *window) if (!pureQt) { FramelessHelperWin::addWindow(window); } - Utils::installSystemMenuHook(winId); + const auto options = qvariant_cast(window->property(kInternalOptionsFlag)); + if (!(options & Option::DontInstallSystemMenuHook)) { + Utils::installSystemMenuHook(winId); + } #endif } @@ -208,7 +212,10 @@ void FramelessWindowsManager::removeWindow(QWindow *window) disconnect(d->win32WorkaroundConnections.value(uuid)); d->win32WorkaroundConnections.remove(uuid); } - Utils::uninstallSystemMenuHook(winId); + const auto options = qvariant_cast(window->property(kInternalOptionsFlag)); + if (!(options & Option::DontInstallSystemMenuHook)) { + Utils::uninstallSystemMenuHook(winId); + } #endif static const bool pureQt = d->usePureQtImplementation(); if (pureQt) { diff --git a/src/core/utils.h b/src/core/utils.h index 74a2038..2fdde37 100644 --- a/src/core/utils.h +++ b/src/core/utils.h @@ -77,6 +77,7 @@ FRAMELESSHELPER_CORE_API void startSystemResize(QWindow *window, const Qt::Edges [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowFixedSize(const QWindow *window); [[nodiscard]] FRAMELESSHELPER_CORE_API QVariant getSystemButtonIconResource (const SystemButtonType button, const SystemTheme theme, const ResourceType type); +FRAMELESSHELPER_CORE_API void sendMouseReleaseEvent(); #ifdef Q_OS_WINDOWS [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin8OrGreater(); @@ -111,6 +112,7 @@ FRAMELESSHELPER_CORE_API void fixupQtInternals(const WId winId); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isFrameBorderColorized(); FRAMELESSHELPER_CORE_API void installSystemMenuHook(const WId winId); FRAMELESSHELPER_CORE_API void uninstallSystemMenuHook(const WId winId); +FRAMELESSHELPER_CORE_API void tryToBeCompatibleWithQtFramelessWindowHint(QWindow *window, const bool enable); #endif // Q_OS_WINDOWS } // namespace Utils diff --git a/src/core/utils_win.cpp b/src/core/utils_win.cpp index ed95b3d..8512ff2 100644 --- a/src/core/utils_win.cpp +++ b/src/core/utils_win.cpp @@ -913,7 +913,9 @@ void Utils::fixupQtInternals(const WId winId) SetLastError(ERROR_SUCCESS); if (SetWindowLongPtrW(hwnd, GWL_STYLE, static_cast(newWindowStyle)) == 0) { qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); + return; } + triggerFrameChange(winId); } void Utils::startSystemMove(QWindow *window) @@ -925,10 +927,7 @@ void Utils::startSystemMove(QWindow *window) #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) window->startSystemMove(); #else - if (ReleaseCapture() == FALSE) { - qWarning() << getSystemErrorMessage(QStringLiteral("ReleaseCapture")); - return; - } + sendMouseReleaseEvent(); const auto hwnd = reinterpret_cast(window->winId()); if (PostMessageW(hwnd, WM_SYSCOMMAND, 0xF012 /*SC_DRAGMOVE*/, 0) == FALSE) { qWarning() << getSystemErrorMessage(QStringLiteral("PostMessageW")); @@ -948,10 +947,7 @@ void Utils::startSystemResize(QWindow *window, const Qt::Edges edges) if (edges == Qt::Edges{}) { return; } - if (ReleaseCapture() == FALSE) { - qWarning() << getSystemErrorMessage(QStringLiteral("ReleaseCapture")); - return; - } + sendMouseReleaseEvent(); const auto hwnd = reinterpret_cast(window->winId()); if (PostMessageW(hwnd, WM_SYSCOMMAND, qtEdgesToWin32Orientation(edges), 0) == FALSE) { qWarning() << getSystemErrorMessage(QStringLiteral("PostMessageW")); @@ -1024,6 +1020,7 @@ void Utils::installSystemMenuHook(const WId winId) return; } g_utilsHelper()->qtWindowProcs.insert(hwnd, originalWindowProc); + //triggerFrameChange(winId); } void Utils::uninstallSystemMenuHook(const WId winId) @@ -1048,6 +1045,40 @@ void Utils::uninstallSystemMenuHook(const WId winId) return; } g_utilsHelper()->qtWindowProcs.remove(hwnd); + //triggerFrameChange(winId); +} + +void Utils::sendMouseReleaseEvent() +{ + if (ReleaseCapture() == FALSE) { + qWarning() << getSystemErrorMessage(QStringLiteral("ReleaseCapture")); + } +} + +void Utils::tryToBeCompatibleWithQtFramelessWindowHint(QWindow *window, const bool enable) +{ + Q_ASSERT(window); + if (!window) { + return; + } + const WId winId = window->winId(); + const auto hwnd = reinterpret_cast(winId); + SetLastError(ERROR_SUCCESS); + const LONG_PTR originalWindowStyle = GetWindowLongPtrW(hwnd, GWL_STYLE); + if (originalWindowStyle == 0) { + qWarning() << getSystemErrorMessage(QStringLiteral("GetWindowLongPtrW")); + return; + } + const Qt::WindowFlags originalWindowFlags = window->flags(); + const Qt::WindowFlags newWindowFlags = (enable ? (originalWindowFlags | Qt::FramelessWindowHint) + : (originalWindowFlags & ~Qt::FramelessWindowHint)); + window->setFlags(newWindowFlags); + SetLastError(ERROR_SUCCESS); + if (SetWindowLongPtrW(hwnd, GWL_STYLE, originalWindowStyle) == 0) { + qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); + return; + } + triggerFrameChange(winId); } FRAMELESSHELPER_END_NAMESPACE diff --git a/src/quick/framelessquickeventfilter.cpp b/src/quick/framelessquickeventfilter.cpp index d097483..560115e 100644 --- a/src/quick/framelessquickeventfilter.cpp +++ b/src/quick/framelessquickeventfilter.cpp @@ -208,6 +208,7 @@ bool FramelessQuickEventFilter::eventFilter(QObject *object, QEvent *event) return false; } const bool titleBar = isInTitleBarDraggableArea(window, scenePos); + const auto options = qvariant_cast(window->property(kInternalOptionsFlag)); switch (eventType) { case QEvent::MouseButtonPress: { if (button != Qt::LeftButton) { @@ -220,6 +221,9 @@ bool FramelessQuickEventFilter::eventFilter(QObject *object, QEvent *event) return true; } case QEvent::MouseButtonRelease: { + if (options & Option::DisableSystemMenu) { + return false; + } if (button != Qt::RightButton) { return false; } @@ -236,6 +240,9 @@ bool FramelessQuickEventFilter::eventFilter(QObject *object, QEvent *event) return true; } case QEvent::MouseButtonDblClick: { + if (options & Option::NoDoubleClickMaximizeToggle) { + return false; + } if (button != Qt::LeftButton) { return false; } diff --git a/src/widgets/framelessmainwindow.cpp b/src/widgets/framelessmainwindow.cpp index 651e6c6..8ffa28a 100644 --- a/src/widgets/framelessmainwindow.cpp +++ b/src/widgets/framelessmainwindow.cpp @@ -27,9 +27,9 @@ FRAMELESSHELPER_BEGIN_NAMESPACE -FramelessMainWindow::FramelessMainWindow(QWidget *parent, Qt::WindowFlags flags) : QMainWindow(parent, flags) +FramelessMainWindow::FramelessMainWindow(QWidget *parent, const Qt::WindowFlags flags, const Options options) : QMainWindow(parent, flags) { - m_helper.reset(new FramelessWidgetsHelper(this, WindowLayout::Custom)); + m_helper.reset(new FramelessWidgetsHelper(this, options)); } FramelessMainWindow::~FramelessMainWindow() = default; @@ -64,12 +64,6 @@ void FramelessMainWindow::toggleMaximized() m_helper->toggleMaximized(); } -void FramelessMainWindow::showEvent(QShowEvent *event) -{ - QMainWindow::showEvent(event); - m_helper->showEventHandler(event); -} - void FramelessMainWindow::changeEvent(QEvent *event) { QMainWindow::changeEvent(event); diff --git a/src/widgets/framelessmainwindow.h b/src/widgets/framelessmainwindow.h index ec892cc..e704a2b 100644 --- a/src/widgets/framelessmainwindow.h +++ b/src/widgets/framelessmainwindow.h @@ -38,7 +38,7 @@ class FRAMELESSHELPER_WIDGETS_API FramelessMainWindow : public QMainWindow Q_PROPERTY(QWidget* titleBarWidget READ titleBarWidget WRITE setTitleBarWidget NOTIFY titleBarWidgetChanged FINAL) public: - explicit FramelessMainWindow(QWidget *parent = nullptr, Qt::WindowFlags flags = {}); + explicit FramelessMainWindow(QWidget *parent = nullptr, const Qt::WindowFlags flags = {}, const Options options = {}); ~FramelessMainWindow() override; Q_NODISCARD Q_INVOKABLE bool isNormal() const; @@ -52,7 +52,6 @@ public: Q_INVOKABLE void toggleMaximized(); protected: - void showEvent(QShowEvent *event) override; void changeEvent(QEvent *event) override; void paintEvent(QPaintEvent *event) override; void mousePressEvent(QMouseEvent *event) override; diff --git a/src/widgets/framelesswidget.cpp b/src/widgets/framelesswidget.cpp index 365c426..2214288 100644 --- a/src/widgets/framelesswidget.cpp +++ b/src/widgets/framelesswidget.cpp @@ -27,9 +27,9 @@ FRAMELESSHELPER_BEGIN_NAMESPACE -FramelessWidget::FramelessWidget(QWidget *parent, const WindowLayout wl) : QWidget(parent) +FramelessWidget::FramelessWidget(QWidget *parent, const Options options) : QWidget(parent) { - m_helper.reset(new FramelessWidgetsHelper(this, wl)); + m_helper.reset(new FramelessWidgetsHelper(this, options)); } FramelessWidget::~FramelessWidget() = default; @@ -74,12 +74,6 @@ void FramelessWidget::toggleMaximized() m_helper->toggleMaximized(); } -void FramelessWidget::showEvent(QShowEvent *event) -{ - QWidget::showEvent(event); - m_helper->showEventHandler(event); -} - void FramelessWidget::changeEvent(QEvent *event) { QWidget::changeEvent(event); diff --git a/src/widgets/framelesswidget.h b/src/widgets/framelesswidget.h index 7c6fa2b..41e02cf 100644 --- a/src/widgets/framelesswidget.h +++ b/src/widgets/framelesswidget.h @@ -39,7 +39,7 @@ class FRAMELESSHELPER_WIDGETS_API FramelessWidget : public QWidget Q_PROPERTY(QWidget* contentWidget READ contentWidget WRITE setContentWidget NOTIFY contentWidgetChanged FINAL) public: - explicit FramelessWidget(QWidget *parent = nullptr, const WindowLayout wl = WindowLayout::Standard); + explicit FramelessWidget(QWidget *parent = nullptr, const Options options = {}); ~FramelessWidget() override; Q_NODISCARD Q_INVOKABLE bool isNormal() const; @@ -56,7 +56,6 @@ public: Q_INVOKABLE void toggleMaximized(); protected: - void showEvent(QShowEvent *event) override; void changeEvent(QEvent *event) override; void paintEvent(QPaintEvent *event) override; void mousePressEvent(QMouseEvent *event) override; diff --git a/src/widgets/framelesswidgetshelper.cpp b/src/widgets/framelesswidgetshelper.cpp index fdd4738..3db2b83 100644 --- a/src/widgets/framelesswidgetshelper.cpp +++ b/src/widgets/framelesswidgetshelper.cpp @@ -23,8 +23,10 @@ */ #include "framelesswidgetshelper.h" +#include #include #include +#include #include #include #include @@ -49,12 +51,12 @@ QPushButton:pressed { } )"); -FramelessWidgetsHelper::FramelessWidgetsHelper(QWidget *q, const WindowLayout wl) : QObject(q) +FramelessWidgetsHelper::FramelessWidgetsHelper(QWidget *q, const Options options) : QObject(q) { Q_ASSERT(q); if (q) { this->q = q; - m_windowLayout = wl; + m_options = options; initialize(); } } @@ -80,7 +82,7 @@ void FramelessWidgetsHelper::setTitleBarWidget(QWidget *widget) if (m_userTitleBarWidget == widget) { return; } - if (isStandardLayout()) { + if (m_options & Option::UseStandardWindowLayout) { if (m_systemTitleBarWidget && m_systemTitleBarWidget->isVisible()) { m_mainLayout->removeWidget(m_systemTitleBarWidget); m_systemTitleBarWidget->hide(); @@ -108,7 +110,7 @@ void FramelessWidgetsHelper::setContentWidget(QWidget *widget) if (!widget) { return; } - if (isCustomLayout()) { + if (!(m_options & Option::UseStandardWindowLayout)) { return; } if (m_userContentWidget == widget) { @@ -143,15 +145,6 @@ void FramelessWidgetsHelper::setHitTestVisible(QWidget *widget, const bool visib } } -void FramelessWidgetsHelper::showEventHandler(QShowEvent *event) -{ - Q_ASSERT(event); - if (!event) { - return; - } - setupFramelessHelperOnce(); -} - void FramelessWidgetsHelper::changeEventHandler(QEvent *event) { Q_ASSERT(event); @@ -162,8 +155,9 @@ void FramelessWidgetsHelper::changeEventHandler(QEvent *event) if ((type != QEvent::WindowStateChange) && (type != QEvent::ActivationChange)) { return; } + const bool standardLayout = (m_options & Option::UseStandardWindowLayout); if (type == QEvent::WindowStateChange) { - if (isStandardLayout()) { + if (standardLayout) { if (isZoomed()) { m_systemMaximizeButton->setToolTip(tr("Restore")); } else { @@ -173,7 +167,7 @@ void FramelessWidgetsHelper::changeEventHandler(QEvent *event) } updateContentsMargins(); } - if (isStandardLayout()) { + if (standardLayout) { updateSystemTitleBarStyleSheet(); } q->update(); @@ -227,6 +221,9 @@ void FramelessWidgetsHelper::mouseReleaseEventHandler(QMouseEvent *event) if (!event) { return; } + if (m_options & Option::DisableSystemMenu) { + return; + } if (event->button() != Qt::RightButton) { return; } @@ -258,6 +255,9 @@ void FramelessWidgetsHelper::mouseDoubleClickEventHandler(QMouseEvent *event) if (!event) { return; } + if (m_options & Option::NoDoubleClickMaximizeToggle) { + return; + } if (event->button() != Qt::LeftButton) { return; } @@ -281,21 +281,35 @@ void FramelessWidgetsHelper::initialize() return; } m_initialized = true; + // Without this flag, Qt will always create an invisible native parent window + // for any native widgets which will intercept some win32 messages and confuse + // our own native event filter, so to prevent some weired bugs from happening, + // just disable this feature. q->setAttribute(Qt::WA_DontCreateNativeAncestors); + // Force the widget become a native window now so that we can deal with its + // win32 events as soon as possible. q->createWinId(); - setupInitialUi(); -} - -void FramelessWidgetsHelper::setupFramelessHelperOnce() -{ - if (m_framelessHelperSetup) { + QWindow * const window = q->windowHandle(); + Q_ASSERT(window); + if (!window) { return; } - m_framelessHelperSetup = true; + window->setProperty(kInternalOptionsFlag, QVariant::fromValue(m_options)); + if (m_options & Option::UseStandardWindowLayout) { + if (q->inherits("QMainWindow")) { + m_options &= ~Options(Option::UseStandardWindowLayout); + qWarning() << "\"Option::UseStandardWindowLayout\" is not compatible with QMainWindow and it's subclasses." + " Enabling this option will mess up with your main window's layout."; + } + } + if (m_options & Option::BeCompatibleWithQtFramelessWindowHint) { + Utils::tryToBeCompatibleWithQtFramelessWindowHint(window, true); + Q_ASSERT(window->flags() & Qt::FramelessWindowHint); + } FramelessWindowsManager *manager = FramelessWindowsManager::instance(); manager->addWindow(q->windowHandle()); connect(manager, &FramelessWindowsManager::systemThemeChanged, this, [this](){ - if (isStandardLayout()) { + if (m_options & Option::UseStandardWindowLayout) { updateSystemTitleBarStyleSheet(); updateSystemButtonsIcon(); q->update(); @@ -305,11 +319,12 @@ void FramelessWidgetsHelper::setupFramelessHelperOnce() connect(manager, &FramelessWindowsManager::systemMenuRequested, this, [this](const QPointF &pos){ QMetaObject::invokeMethod(q, "systemMenuRequested", Q_ARG(QPointF, pos)); }); + setupInitialUi(); } void FramelessWidgetsHelper::createSystemTitleBar() { - if (isCustomLayout()) { + if (!(m_options & Option::UseStandardWindowLayout)) { return; } m_systemTitleBarWidget = new QWidget(q); @@ -352,7 +367,7 @@ void FramelessWidgetsHelper::createSystemTitleBar() void FramelessWidgetsHelper::createUserContentContainer() { - if (isCustomLayout()) { + if (!(m_options & Option::UseStandardWindowLayout)) { return; } m_userContentContainerWidget = new QWidget(q); @@ -365,7 +380,7 @@ void FramelessWidgetsHelper::createUserContentContainer() void FramelessWidgetsHelper::setupInitialUi() { - if (isStandardLayout()) { + if (m_options & Option::UseStandardWindowLayout) { createSystemTitleBar(); createUserContentContainer(); m_mainLayout = new QVBoxLayout(q); @@ -402,7 +417,7 @@ bool FramelessWidgetsHelper::isInTitleBarDraggableArea(const QPoint &pos) const } return region; } - if (isStandardLayout()) { + if (m_options & Option::UseStandardWindowLayout) { QRegion region = mapGeometryToScene(m_systemTitleBarWidget); region -= mapGeometryToScene(m_systemMinimizeButton); region -= mapGeometryToScene(m_systemMaximizeButton); @@ -417,22 +432,13 @@ bool FramelessWidgetsHelper::isInTitleBarDraggableArea(const QPoint &pos) const bool FramelessWidgetsHelper::shouldDrawFrameBorder() const { #ifdef Q_OS_WINDOWS - return (Utils::isWindowFrameBorderVisible() && !Utils::isWin11OrGreater() && isNormal()); + return (Utils::isWindowFrameBorderVisible() && !Utils::isWin11OrGreater() + && isNormal() && !(m_options & Option::DontDrawTopWindowFrameBorder)); #else return false; #endif } -bool FramelessWidgetsHelper::isStandardLayout() const -{ - return (m_windowLayout == WindowLayout::Standard); -} - -bool FramelessWidgetsHelper::isCustomLayout() const -{ - return (m_windowLayout == WindowLayout::Custom); -} - bool FramelessWidgetsHelper::shouldIgnoreMouseEvents(const QPoint &pos) const { return (isNormal() && ((pos.x() < kDefaultResizeBorderThickness) @@ -449,7 +455,7 @@ void FramelessWidgetsHelper::updateContentsMargins() void FramelessWidgetsHelper::updateSystemTitleBarStyleSheet() { - if (isCustomLayout()) { + if (!(m_options & Option::UseStandardWindowLayout)) { return; } const bool active = q->isActiveWindow(); @@ -484,7 +490,7 @@ void FramelessWidgetsHelper::updateSystemTitleBarStyleSheet() void FramelessWidgetsHelper::updateSystemButtonsIcon() { - if (isCustomLayout()) { + if (!(m_options & Option::UseStandardWindowLayout)) { return; } const SystemTheme theme = ((Utils::shouldAppsUseDarkMode() || Utils::isTitleBarColorized()) ? SystemTheme::Dark : SystemTheme::Light); diff --git a/src/widgets/framelesswidgetshelper.h b/src/widgets/framelesswidgetshelper.h index 402b49a..8e8a927 100644 --- a/src/widgets/framelesswidgetshelper.h +++ b/src/widgets/framelesswidgetshelper.h @@ -45,7 +45,7 @@ class FRAMELESSHELPER_WIDGETS_API FramelessWidgetsHelper : public QObject Q_DISABLE_COPY_MOVE(FramelessWidgetsHelper) public: - explicit FramelessWidgetsHelper(QWidget *q, const WindowLayout wl); + explicit FramelessWidgetsHelper(QWidget *q, const Options options = {}); ~FramelessWidgetsHelper() override; Q_NODISCARD Q_INVOKABLE bool isNormal() const; @@ -61,7 +61,6 @@ public: Q_INVOKABLE void toggleMaximized(); - Q_INVOKABLE void showEventHandler(QShowEvent *event); Q_INVOKABLE void changeEventHandler(QEvent *event); Q_INVOKABLE void paintEventHandler(QPaintEvent *event); Q_INVOKABLE void mousePressEventHandler(QMouseEvent *event); @@ -70,14 +69,11 @@ public: private: void initialize(); - void setupFramelessHelperOnce(); void createSystemTitleBar(); void createUserContentContainer(); void setupInitialUi(); Q_NODISCARD bool isInTitleBarDraggableArea(const QPoint &pos) const; Q_NODISCARD bool shouldDrawFrameBorder() const; - Q_NODISCARD bool isStandardLayout() const; - Q_NODISCARD bool isCustomLayout() const; Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const; private Q_SLOTS: @@ -88,8 +84,7 @@ private Q_SLOTS: private: QWidget *q = nullptr; bool m_initialized = false; - bool m_framelessHelperSetup = false; - WindowLayout m_windowLayout = WindowLayout::Standard; + Options m_options = {}; QWidget *m_systemTitleBarWidget = nullptr; QLabel *m_systemWindowTitleLabel = nullptr; QPushButton *m_systemMinimizeButton = nullptr;