diff --git a/examples/dialog/dialog.cpp b/examples/dialog/dialog.cpp index eebda61..de80b32 100644 --- a/examples/dialog/dialog.cpp +++ b/examples/dialog/dialog.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,14 @@ void Dialog::setupUi() findButton = new QPushButton(tr("&Find")); findButton->setDefault(true); + connect(findButton, &QPushButton::clicked, this, [this](){ + const QString text = lineEdit->text(); + if (text.isEmpty()) { + QMessageBox::warning(this, tr("Warning"), tr("You didn't enter anything in the search box.")); + } else { + QMessageBox::information(this, tr("Result"), tr("You wanted to find: \"%1\".").arg(text)); + } + }); moreButton = new QPushButton(tr("&More")); moreButton->setCheckable(true); diff --git a/include/FramelessHelper/Quick/private/quickstandardtitlebar_p.h b/include/FramelessHelper/Quick/private/quickstandardtitlebar_p.h index 98147a6..2215fdc 100644 --- a/include/FramelessHelper/Quick/private/quickstandardtitlebar_p.h +++ b/include/FramelessHelper/Quick/private/quickstandardtitlebar_p.h @@ -113,7 +113,7 @@ Q_SIGNALS: private: void initialize(); void updateAll(); - void mouseEventHandler(const QMouseEvent *event); + Q_NODISCARD bool mouseEventHandler(QMouseEvent *event); Q_NODISCARD QRect windowIconRect() const; Q_NODISCARD bool isInTitleBarIconArea(const QPoint &pos) const; Q_NODISCARD bool windowIconVisible_real() const; diff --git a/include/FramelessHelper/Widgets/private/standardtitlebar_p.h b/include/FramelessHelper/Widgets/private/standardtitlebar_p.h index 1501f04..35366c3 100644 --- a/include/FramelessHelper/Widgets/private/standardtitlebar_p.h +++ b/include/FramelessHelper/Widgets/private/standardtitlebar_p.h @@ -77,7 +77,7 @@ public: Q_NODISCARD QFont titleFont() const; void setTitleFont(const QFont &value); - void mouseEventHandler(const QMouseEvent *event); + Q_NODISCARD bool mouseEventHandler(QMouseEvent *event); Q_NODISCARD QRect windowIconRect() const; Q_NODISCARD bool windowIconVisible_real() const; diff --git a/src/core/cmakehelper.cmake b/src/core/cmakehelper.cmake index 28c1daf..f453a01 100644 --- a/src/core/cmakehelper.cmake +++ b/src/core/cmakehelper.cmake @@ -38,35 +38,39 @@ function(setup_compile_params arg_target) QT_DISABLE_DEPRECATED_BEFORE=0x070000 QT_DISABLE_DEPRECATED_UP_TO=0x070000 # Since 6.5 ) - if(MSVC) + if(WIN32) # Needed by both MSVC and MinGW set(_WIN32_WINNT_WIN10 0x0A00) set(NTDDI_WIN10_CO 0x0A00000B) + target_compile_definitions(${arg_target} PRIVATE + WINVER=${_WIN32_WINNT_WIN10} _WIN32_WINNT=${_WIN32_WINNT_WIN10} + _WIN32_IE=${_WIN32_WINNT_WIN10} NTDDI_VERSION=${NTDDI_WIN10_CO} + ) + endif() + if(MSVC) target_compile_definitions(${arg_target} PRIVATE _CRT_NON_CONFORMING_SWPRINTFS _CRT_SECURE_NO_WARNINGS _CRT_SECURE_NO_DEPRECATE _CRT_NONSTDC_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE _ENABLE_EXTENDED_ALIGNED_STORAGE NOMINMAX UNICODE _UNICODE WIN32_LEAN_AND_MEAN WINRT_LEAN_AND_MEAN - WINVER=${_WIN32_WINNT_WIN10} _WIN32_WINNT=${_WIN32_WINNT_WIN10} - _WIN32_IE=${_WIN32_WINNT_WIN10} NTDDI_VERSION=${NTDDI_WIN10_CO} ) target_compile_options(${arg_target} PRIVATE - /utf-8 /W3 /WX # Cannot use /W4 here, Qt's own headers are not warning-clean. + /utf-8 /W3 /WX # Can't use /W4 here, Qt's own headers are not warning-clean, especially QtQuick headers. $<$:/JMC> - $<$>:/guard:cf /Gw /Gy /QIntel-jcc-erratum /Zc:inline> # /guard:ehcont ? /Qspectre-load ? + $<$>:/guard:cf /Gw /Gy /QIntel-jcc-erratum /Zc:inline> # /guard:ehcont? /Qspectre-load? ) target_link_options(${arg_target} PRIVATE /WX # Make sure we don't use wrong parameters. - $<$>:/CETCOMPAT /GUARD:CF /OPT:REF /OPT:ICF> # /GUARD:EHCONT ? + $<$>:/CETCOMPAT /GUARD:CF /OPT:REF /OPT:ICF> # /GUARD:EHCONT? ) else() target_compile_options(${arg_target} PRIVATE -Wall -Wextra -Werror - #$<$>:-ffunction-sections -fdata-sections -fcf-protection=full -Wa,-mno-branches-within-32B-boundaries> + $<$>:-ffunction-sections -fdata-sections -fcf-protection=full> # -Wa,-mno-branches-within-32B-boundaries? ) - #[[target_link_options(${arg_target} PRIVATE + target_link_options(${arg_target} PRIVATE $<$>:-Wl,--gc-sections> ) - if(CLANG) + #[[if(CLANG) target_compile_options(${arg_target} PRIVATE $<$>:-Xclang -cfguard -mretpoline> ) diff --git a/src/core/framelesshelper_qt.cpp b/src/core/framelesshelper_qt.cpp index f6a5238..6c8d61f 100644 --- a/src/core/framelesshelper_qt.cpp +++ b/src/core/framelesshelper_qt.cpp @@ -122,24 +122,24 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event) managerPriv->notifySystemThemeHasChangedOrNot(); } } - return false; + return QObject::eventFilter(object, event); } // We are only interested in events that are dispatched to top level windows. if (!object->isWindowType()) { - return false; + return QObject::eventFilter(object, event); } const QEvent::Type type = event->type(); // We are only interested in some specific mouse events. if ((type != QEvent::MouseButtonPress) && (type != QEvent::MouseButtonRelease) && (type != QEvent::MouseButtonDblClick) && (type != QEvent::MouseMove)) { - return false; + return QObject::eventFilter(object, event); } const auto window = qobject_cast(object); const WId windowId = window->winId(); g_qtHelper()->mutex.lock(); if (!g_qtHelper()->data.contains(windowId)) { g_qtHelper()->mutex.unlock(); - return false; + return QObject::eventFilter(object, event); } const QtHelperData data = g_qtHelper()->data.value(windowId); g_qtHelper()->mutex.unlock(); @@ -166,6 +166,7 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event) const Qt::Edges edges = Utils::calculateWindowEdges(window, scenePos); if (edges != Qt::Edges{}) { Utils::startSystemResize(window, edges, globalPos); + event->accept(); return true; } } @@ -179,6 +180,7 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event) if (button == Qt::RightButton) { if (!ignoreThisEvent && insideTitleBar) { data.params.showSystemMenu(scenePos); + event->accept(); return true; } } @@ -190,6 +192,8 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event) newWindowState = Qt::WindowMaximized; } data.params.setWindowState(newWindowState); + event->accept(); + return true; } } break; case QEvent::MouseMove: { @@ -210,6 +214,7 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event) if (data.leftButtonPressed) { if (!ignoreThisEvent && insideTitleBar) { Utils::startSystemMove(window, globalPos); + event->accept(); return true; } } @@ -217,7 +222,7 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event) default: break; } - return false; + return QObject::eventFilter(object, event); } FRAMELESSHELPER_END_NAMESPACE diff --git a/src/core/utils_win.cpp b/src/core/utils_win.cpp index 1f797ba..967bf05 100644 --- a/src/core/utils_win.cpp +++ b/src/core/utils_win.cpp @@ -764,6 +764,11 @@ void Utils::showSystemMenu(const WId windowId, const QPoint &pos, const bool sel // Popup the system menu at the required position. const int result = TrackPopupMenu(hMenu, (TPM_RETURNCMD | (QGuiApplication::isRightToLeft() ? TPM_RIGHTALIGN : TPM_LEFTALIGN)), pos.x(), pos.y(), 0, hWnd, nullptr); + + // Unhighlight the first menu item after the popup menu is closed, otherwise it will keep + // highlighting until we unhighlight it manually. + HiliteMenuItem(hWnd, hMenu, SC_RESTORE, (MF_BYCOMMAND | MFS_UNHILITE)); + if (result == 0) { // The user canceled the menu, no need to continue. return; diff --git a/src/quick/framelessquickhelper.cpp b/src/quick/framelessquickhelper.cpp index 1dc8991..a99507d 100644 --- a/src/quick/framelessquickhelper.cpp +++ b/src/quick/framelessquickhelper.cpp @@ -801,19 +801,35 @@ FramelessQuickHelper *FramelessQuickHelper::get(QObject *object) if (!object) { return nullptr; } - FramelessQuickHelper *instance = nullptr; QObject *parent = nullptr; - if (const auto item = qobject_cast(object)) { - parent = ((item->window() && item->window()->contentItem()) ? item->window()->contentItem() : item); + QQuickItem *parentItem = nullptr; + if (const auto window = qobject_cast(object)) { + if (QQuickItem * const item = window->contentItem()) { + parent = item; + parentItem = item; + } else { + parent = window; + } + } else if (const auto item = qobject_cast(object)) { + if (QQuickWindow * const window = item->window()) { + if (QQuickItem * const contentItem = window->contentItem()) { + parent = contentItem; + parentItem = contentItem; + } else { + parent = window; + parentItem = item; + } + } else { + parent = item; + parentItem = item; + } } else { parent = object; } - instance = parent->findChild(); + FramelessQuickHelper *instance = parent->findChild(); if (!instance) { instance = new FramelessQuickHelper; - if (const auto item = qobject_cast(parent)) { - instance->setParentItem(item); - } + instance->setParentItem(parentItem); instance->setParent(parent); // No need to do this here, we'll do it once the item has been assigned to a specific window. //instance->d_func()->attachToWindow(); @@ -943,14 +959,19 @@ void FramelessQuickHelper::itemChange(const ItemChange change, const ItemChangeD { QQuickItem::itemChange(change, value); if ((change == ItemSceneChange) && value.window) { + const QObject * const p = parent(); + const QQuickItem * const pItem = parentItem(); QQuickItem * const rootItem = value.window->contentItem(); if (rootItem) { - if ((parentItem() != rootItem) || (parent() != rootItem)) { + if ((pItem != rootItem) || (p != rootItem)) { setParentItem(rootItem); setParent(rootItem); } } else { - if (parent() != value.window) { + if (pItem != nullptr) { + setParentItem(nullptr); + } + if (p != value.window) { setParent(value.window); } } diff --git a/src/quick/quickstandardtitlebar.cpp b/src/quick/quickstandardtitlebar.cpp index 71c0ae0..d73f711 100644 --- a/src/quick/quickstandardtitlebar.cpp +++ b/src/quick/quickstandardtitlebar.cpp @@ -357,11 +357,11 @@ void QuickStandardTitleBar::updateWindowIcon() m_windowIcon->setSource(icon); } -void QuickStandardTitleBar::mouseEventHandler(const QMouseEvent *event) +bool QuickStandardTitleBar::mouseEventHandler(QMouseEvent *event) { Q_ASSERT(event); if (!event) { - return; + return false; } const Qt::MouseButton button = event->button(); #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) @@ -402,6 +402,7 @@ void QuickStandardTitleBar::mouseEventHandler(const QMouseEvent *event) return scenePos; }()); }); + // Don't eat this event, we have not handled it yet. } break; case QEvent::MouseButtonDblClick: @@ -409,12 +410,16 @@ void QuickStandardTitleBar::mouseEventHandler(const QMouseEvent *event) if ((button == Qt::LeftButton) && interestArea) { m_closeTriggered = true; w->close(); + // Eat this event, we have handled it here. + event->accept(); + return true; } } break; default: break; } + return false; } QRect QuickStandardTitleBar::windowIconRect() const @@ -521,6 +526,8 @@ void QuickStandardTitleBar::itemChange(const ItemChange change, const ItemChange m_windowTitleChangeConnection = connect(value.window, &QQuickWindow::windowTitleChanged, this, &QuickStandardTitleBar::updateTitleLabelText); updateAll(); value.window->installEventFilter(this); + // The window has changed, we need to re-add or re-remove the window icon rect to + // the hit test visible whitelist. This is different with Qt Widgets. FramelessQuickHelper::get(this)->setHitTestVisible(windowIconRect(), windowIconVisible_real()); } } @@ -542,9 +549,14 @@ bool QuickStandardTitleBar::eventFilter(QObject *object, QEvent *event) const QEvent::Type type = event->type(); if (type == QEvent::LanguageChange) { retranslateUi(); + // Don't eat the event here, we need it to keep dispatching to other + // objects that may be interested in this event. } else if ((type >= QEvent::MouseButtonPress) && (type <= QEvent::MouseMove)) { const auto mouseEvent = static_cast(event); - mouseEventHandler(mouseEvent); + if (mouseEventHandler(mouseEvent)) { + // We have handled the event already, stop dispatching. + return true; + } } return QQuickRectangle::eventFilter(object, event); } diff --git a/src/widgets/framelesshelperwidgets_global.cpp b/src/widgets/framelesshelperwidgets_global.cpp index c73793a..b644b7a 100644 --- a/src/widgets/framelesshelperwidgets_global.cpp +++ b/src/widgets/framelesshelperwidgets_global.cpp @@ -28,12 +28,14 @@ #include "framelesswidgetshelper.h" #include "framelesswidget.h" #include "framelessmainwindow.h" +#include "framelessdialog.h" #include "widgetssharedhelper_p.h" #include "standardtitlebar_p.h" #include "standardsystembutton_p.h" #include "framelesswidgetshelper_p.h" #include "framelesswidget_p.h" #include "framelessmainwindow_p.h" +#include "framelessdialog_p.h" FRAMELESSHELPER_BEGIN_NAMESPACE @@ -62,12 +64,14 @@ void initialize() qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); + qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); + qRegisterMetaType(); #endif } diff --git a/src/widgets/framelesswidgetshelper.cpp b/src/widgets/framelesswidgetshelper.cpp index 2c50d53..54f95a9 100644 --- a/src/widgets/framelesswidgetshelper.cpp +++ b/src/widgets/framelesswidgetshelper.cpp @@ -421,15 +421,10 @@ void FramelessWidgetsHelperPrivate::attachToWindow() QWidget *FramelessWidgetsHelperPrivate::getWindow() const { Q_Q(const FramelessWidgetsHelper); - const auto parentWidget = qobject_cast(q->parent()); - if (!parentWidget) { - return nullptr; + if (const auto parentWidget = qobject_cast(q->parent())) { + return (parentWidget->nativeParentWidget() ? parentWidget->nativeParentWidget() : parentWidget->window()); } - QWidget * const nativeParentWidget = parentWidget->nativeParentWidget(); - if (nativeParentWidget) { - return nativeParentWidget; - } - return parentWidget->window(); + return nullptr; } WidgetsHelperData FramelessWidgetsHelperPrivate::getWindowData() const @@ -782,15 +777,13 @@ FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *object) if (!object) { return nullptr; } - FramelessWidgetsHelper *instance = nullptr; QObject *parent = nullptr; - if (object->isWidgetType()) { - const auto widget = qobject_cast(object); + if (const auto widget = qobject_cast(object)) { parent = (widget->nativeParentWidget() ? widget->nativeParentWidget() : widget->window()); } else { parent = object; } - instance = parent->findChild(); + FramelessWidgetsHelper *instance = parent->findChild(); if (!instance) { instance = new FramelessWidgetsHelper(parent); instance->d_func()->attachToWindow(); diff --git a/src/widgets/standardsystembutton.cpp b/src/widgets/standardsystembutton.cpp index 57f2a95..68ec75e 100644 --- a/src/widgets/standardsystembutton.cpp +++ b/src/widgets/standardsystembutton.cpp @@ -25,6 +25,7 @@ #include "standardsystembutton.h" #include "standardsystembutton_p.h" #include +#include #include #include #include @@ -314,6 +315,7 @@ void StandardSystemButtonPrivate::enterEventHandler(QT_ENTER_EVENT_TYPE *event) return; } setHovered(true); + event->accept(); } void StandardSystemButtonPrivate::leaveEventHandler(QEvent *event) @@ -323,6 +325,7 @@ void StandardSystemButtonPrivate::leaveEventHandler(QEvent *event) return; } setHovered(false); + event->accept(); } void StandardSystemButtonPrivate::paintEventHandler(QPaintEvent *event) @@ -373,6 +376,7 @@ void StandardSystemButtonPrivate::paintEventHandler(QPaintEvent *event) painter.drawText(buttonRect, Qt::AlignCenter, m_code); } painter.restore(); + event->accept(); } void StandardSystemButtonPrivate::initialize() diff --git a/src/widgets/standardtitlebar.cpp b/src/widgets/standardtitlebar.cpp index c4216b1..ae5ea69 100644 --- a/src/widgets/standardtitlebar.cpp +++ b/src/widgets/standardtitlebar.cpp @@ -126,7 +126,10 @@ ChromePalette *StandardTitleBarPrivate::chromePalette() const void StandardTitleBarPrivate::paintTitleBar(QPaintEvent *event) { - Q_UNUSED(event); + Q_ASSERT(event); + if (!event) { + return; + } Q_Q(StandardTitleBar); if (!m_window || m_chromePalette.isNull()) { return; @@ -181,6 +184,7 @@ void StandardTitleBarPrivate::paintTitleBar(QPaintEvent *event) } } painter.restore(); + event->accept(); } bool StandardTitleBarPrivate::titleLabelVisible() const @@ -230,6 +234,13 @@ void StandardTitleBarPrivate::setWindowIconVisible(const bool value) return; } m_windowIconVisible = value; + // Ideally we should use FramelessWidgetsHelper::get(this) everywhere, but sadly when + // we call it here, it may be too early that FramelessWidgetsHelper has not attached + // to the top level widget yet, and thus it will trigger an assert error (the assert + // should not be suppressed, because it usually indicates there's something really + // wrong). So here we have to use the top level widget directly, as a special case. + // NOTE: In your own code, you should always use FramelessWidgetsHelper::get(this) + // if possible. FramelessWidgetsHelper::get(m_window)->setHitTestVisible(windowIconRect(), windowIconVisible_real()); Q_Q(StandardTitleBar); q->update(); @@ -252,14 +263,11 @@ void StandardTitleBarPrivate::setTitleFont(const QFont &value) Q_EMIT q->titleFontChanged(); } -void StandardTitleBarPrivate::mouseEventHandler(const QMouseEvent *event) +bool StandardTitleBarPrivate::mouseEventHandler(QMouseEvent *event) { Q_ASSERT(event); if (!event) { - return; - } - if (!m_window) { - return; + return false; } Q_Q(const StandardTitleBar); const Qt::MouseButton button = event->button(); @@ -271,7 +279,8 @@ void StandardTitleBarPrivate::mouseEventHandler(const QMouseEvent *event) const bool interestArea = isInTitleBarIconArea(scenePos); switch (event->type()) { case QEvent::MouseButtonRelease: - if (interestArea) { + // We need a valid top level widget here. + if (m_window && interestArea) { // Sadly the mouse release events are always triggered before the // mouse double click events, and if we intercept the mouse release // events here, we'll never get the double click events afterwards, @@ -294,6 +303,7 @@ void StandardTitleBarPrivate::mouseEventHandler(const QMouseEvent *event) if (m_closeTriggered) { return; } + // Please refer to the comments in StandardTitleBarPrivate::setWindowIconVisible(). FramelessWidgetsHelper::get(m_window)->showSystemMenu([button, q, &scenePos]() -> QPoint { if (button == Qt::LeftButton) { return {0, q->height()}; @@ -301,17 +311,23 @@ void StandardTitleBarPrivate::mouseEventHandler(const QMouseEvent *event) return scenePos; }()); }); + // Don't eat this event, we have not handled it yet. } break; case QEvent::MouseButtonDblClick: - if ((button == Qt::LeftButton) && interestArea) { + // We need a valid top level widget here. + if (m_window && (button == Qt::LeftButton) && interestArea) { m_closeTriggered = true; m_window->close(); + // Eat this event, we have handled it here. + event->accept(); + return true; } break; default: break; } + return false; } QRect StandardTitleBarPrivate::windowIconRect() const @@ -604,14 +620,14 @@ void StandardTitleBar::mouseReleaseEvent(QMouseEvent *event) { QWidget::mouseReleaseEvent(event); Q_D(StandardTitleBar); - d->mouseEventHandler(event); + Q_UNUSED(d->mouseEventHandler(event)); } void StandardTitleBar::mouseDoubleClickEvent(QMouseEvent *event) { QWidget::mouseDoubleClickEvent(event); Q_D(StandardTitleBar); - d->mouseEventHandler(event); + Q_UNUSED(d->mouseEventHandler(event)); } FRAMELESSHELPER_END_NAMESPACE diff --git a/src/widgets/widgetssharedhelper.cpp b/src/widgets/widgetssharedhelper.cpp index 46d3eb5..bb07546 100644 --- a/src/widgets/widgetssharedhelper.cpp +++ b/src/widgets/widgetssharedhelper.cpp @@ -121,15 +121,15 @@ bool WidgetsSharedHelper::eventFilter(QObject *object, QEvent *event) const auto paintEvent = static_cast(event); paintEventHandler(paintEvent); } break; - case QEvent::WindowStateChange: { + case QEvent::WindowStateChange: changeEventHandler(event); - } break; + break; case QEvent::Move: - case QEvent::Resize: { + case QEvent::Resize: if (m_micaEnabled) { m_targetWidget->update(); } - } break; + break; default: break; } @@ -179,7 +179,10 @@ void WidgetsSharedHelper::changeEventHandler(QEvent *event) void WidgetsSharedHelper::paintEventHandler(QPaintEvent *event) { - Q_UNUSED(event); + Q_ASSERT(event); + if (!event) { + return; + } if (m_micaEnabled && m_micaMaterial) { QPainter painter(m_targetWidget); m_micaMaterial->paint(&painter, m_targetWidget->size(), @@ -205,6 +208,8 @@ void WidgetsSharedHelper::paintEventHandler(QPaintEvent *event) painter.restore(); } #endif + // Don't eat this event here, we need Qt to keep dispatching this paint event + // otherwise the widget won't paint anything else from the user side. } bool WidgetsSharedHelper::shouldDrawFrameBorder() const