From 2850756411e629eee3827f140d36e94d84ff3cc6 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Thu, 9 Oct 2025 20:03:46 +0200 Subject: [PATCH 1/3] QToolButton: don't override user-set popup mode The attempt to unify the popup mode for a QAction with a QMenu resulted in the ignorance of the popup mode set by the user. Therefore add some logic to know if the mode can be set to the 'new' default mode when the QAction has a corresponding QMenu. This amends 0f6135ae6d97f77c1a737a26f67bf3242940e11c Pick-to: 6.10 Task-number: QTBUG-140148 Fixes: QTBUG-140793 Change-Id: Ie093744f1474dbd8c00112345e77497cc3177b2c Reviewed-by: Volker Hilsheimer --- src/widgets/widgets/qtoolbutton.cpp | 21 ++++-- .../widgets/qtoolbutton/tst_qtoolbutton.cpp | 74 +++++++++++++++++++ 2 files changed, 88 insertions(+), 7 deletions(-) diff --git a/src/widgets/widgets/qtoolbutton.cpp b/src/widgets/widgets/qtoolbutton.cpp index e6f7edc1ac12..01150cd39b23 100644 --- a/src/widgets/widgets/qtoolbutton.cpp +++ b/src/widgets/widgets/qtoolbutton.cpp @@ -58,7 +58,8 @@ class QToolButtonPrivate : public QAbstractButtonPrivate Qt::ArrowType arrowType; Qt::ToolButtonStyle toolButtonStyle; QToolButton::ToolButtonPopupMode popupMode; - enum { NoButtonPressed=0, MenuButtonPressed=1, ToolButtonPressed=2 }; + uint popupModeSetByUser : 1; // true if popupMode was set through setPopupMode + enum { NoButtonPressed = 0, MenuButtonPressed = 1, ToolButtonPressed = 2 }; uint buttonPressed : 2; uint menuButtonDown : 1; uint autoRaise : 1; @@ -178,6 +179,7 @@ void QToolButtonPrivate::init() arrowType = Qt::NoArrow; menuButtonDown = false; popupMode = QToolButton::DelayedPopup; + popupModeSetByUser = false; buttonPressed = QToolButtonPrivate::NoButtonPressed; toolButtonStyle = Qt::ToolButtonIconOnly; @@ -827,7 +829,7 @@ void QToolButtonPrivate::onMenuTriggered(QAction *action) void QToolButtonPrivate::onDefaultActionChanged() { Q_Q(QToolButton); - if (defaultAction && defaultAction->menu()) + if (defaultAction && defaultAction->menu() && !popupModeSetByUser) q->setPopupMode(QToolButton::MenuButtonPopup); } @@ -864,6 +866,7 @@ void QToolButtonPrivate::onDefaultActionChanged() void QToolButton::setPopupMode(ToolButtonPopupMode mode) { Q_D(QToolButton); + d->popupModeSetByUser = true; d->popupMode = mode; } @@ -922,10 +925,11 @@ void QToolButton::setDefaultAction(QAction *action) { Q_D(QToolButton); #if QT_CONFIG(menu) - if (d->defaultAction) { + if (d->defaultAction && d->defaultAction != action) { QObjectPrivate::disconnect(d->defaultAction, &QAction::changed, d, &QToolButtonPrivate::onDefaultActionChanged); } + const bool hadMenu = d->hasMenu(); #endif d->defaultAction = action; if (!action) @@ -949,15 +953,18 @@ void QToolButton::setDefaultAction(QAction *action) setWhatsThis(action->whatsThis()); #endif #if QT_CONFIG(menu) - if (action->menu()) { + if (!hadMenu && !d->popupModeSetByUser) { // ### Qt7 Fixme // new 'default' popup mode defined introduced by tool bar. We // should have changed QToolButton's default instead. Do that // in 4.2. - setPopupMode(QToolButton::MenuButtonPopup); + if (action->menu()) { + setPopupMode(QToolButton::MenuButtonPopup); + } else { + QObjectPrivate::connect(d->defaultAction, &QAction::changed, d, + &QToolButtonPrivate::onDefaultActionChanged); + } } - QObjectPrivate::connect(d->defaultAction, &QAction::changed, d, - &QToolButtonPrivate::onDefaultActionChanged); #endif setCheckable(action->isCheckable()); setChecked(action->isChecked()); diff --git a/tests/auto/widgets/widgets/qtoolbutton/tst_qtoolbutton.cpp b/tests/auto/widgets/widgets/qtoolbutton/tst_qtoolbutton.cpp index 9588f1933875..8721a0a16574 100644 --- a/tests/auto/widgets/widgets/qtoolbutton/tst_qtoolbutton.cpp +++ b/tests/auto/widgets/widgets/qtoolbutton/tst_qtoolbutton.cpp @@ -36,6 +36,7 @@ private slots: void defaultActionSynced(); void deleteInHandler(); void emptyMenu(); + void popupMode(); protected slots: void sendMouseClick(); @@ -361,5 +362,78 @@ void tst_QToolButton::emptyMenu() QTRY_COMPARE(triggeredSpy.size(), 1); } +void tst_QToolButton::popupMode() +{ + { + // action without menu -> no change in popup mode + QToolButton tb; + QCOMPARE(tb.popupMode(), QToolButton::DelayedPopup); // ### Qt7 change to MenuButtonPopup + auto a = new QAction("Action 1"); + tb.setDefaultAction(a); + QCOMPARE(tb.popupMode(), QToolButton::DelayedPopup); + } + { + // action with menu, no user-set popup mode -> MenuButtonPopup + QToolButton tb; + auto a = new QAction("Action 1"); + auto menu = new QMenu; + menu->addAction("Menuaction"); + a->setMenu(menu); // before setDefaultAction + tb.setDefaultAction(a); + QCOMPARE(tb.popupMode(), QToolButton::MenuButtonPopup); + tb.setPopupMode(QToolButton::InstantPopup); + QCOMPARE(tb.popupMode(), QToolButton::InstantPopup); + } + { + // action with menu, no user-set popup mode -> MenuButtonPopup + QToolButton tb; + auto a = new QAction("Action 1"); + auto menu = new QMenu; + menu->addAction("Menuaction"); + tb.setDefaultAction(a); + a->setMenu(menu); // after setDefaultAction + QCOMPARE(tb.popupMode(), QToolButton::MenuButtonPopup); + tb.setPopupMode(QToolButton::InstantPopup); + QCOMPARE(tb.popupMode(), QToolButton::InstantPopup); + } + { + // action without (initial) menu, user-set popup mode is not changed + QToolButton tb; + auto a = new QAction("Action 1"); + auto menu = new QMenu; + menu->addAction("Menuaction"); + tb.setDefaultAction(a); + tb.setPopupMode(QToolButton::InstantPopup); + QCOMPARE(tb.popupMode(), QToolButton::InstantPopup); + a->setMenu(menu); + QCOMPARE(tb.popupMode(), QToolButton::InstantPopup); + } + { + // action with menu but after user-set popup mode -> popup mode is not changed + QToolButton tb; + auto a = new QAction("Action 1"); + auto menu = new QMenu; + menu->addAction("Menuaction"); + tb.setPopupMode(QToolButton::InstantPopup); + QCOMPARE(tb.popupMode(), QToolButton::InstantPopup); + a->setMenu(menu); // before setDefaultAction + tb.setDefaultAction(a); + QCOMPARE(tb.popupMode(), QToolButton::InstantPopup); + } + { + // action with menu but after user-set popup mode -> popup mode is not changed + QToolButton tb; + auto a = new QAction("Action 1"); + auto menu = new QMenu; + menu->addAction("Menuaction"); + tb.setPopupMode(QToolButton::InstantPopup); + QCOMPARE(tb.popupMode(), QToolButton::InstantPopup); + tb.setDefaultAction(a); + a->setMenu(menu); // after setDefaultAction + QCOMPARE(tb.popupMode(), QToolButton::InstantPopup); + } +} + + QTEST_MAIN(tst_QToolButton) #include "tst_qtoolbutton.moc" From 08ebe3465cc2fce98662b5833b75503490f66265 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 5 Sep 2025 09:18:24 -0700 Subject: [PATCH 2/3] qnumeric.h: add support for C23/C++26 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows us to have cross-platform support instead of relying on intrinsics. Though this requires banning 'char' because this type changes signedness I guess and C23 doesn't allow them. Both Clang and GCC have a C23 header for , added for GCC 14 and LLVM 19. Both sets of headers work in earlier C versions. Neither MSVC nor MS STL have added the headers, but once they do we should get the proper constexpr and runtime-optimal content that they came close to adding[1], where I reported: > For my own codebase, I don’t plan on writing new code. Users of Visual > Studio will keep getting worse codegen for a few more years, unless > they switch to Clang or GCC. GCC's header does not work in C++ due to the use of _Bool. That is fixed by libstdc++ from GCC15, with a C++ , so we require that version of GCC. This will fail to compile with GCC and libc++ until the bug[2] is fixed. [ChangeLog][Source-Incompatible Changes] qnumeric.h's qAddOverflow(), qSubOverFlow(), and qMulOverflow() functions no longer accept char as a template parameter type. Use quint8 or qint8. [1] https://developercommunity.visualstudio.com/t/please-implement-integer-overflow-detection/409051 [2] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121811 Change-Id: I5ca93cf4166735075b1bfffdad1dcd7c20cab031 Reviewed-by: Ahmad Samir --- src/corelib/global/qnumeric.h | 49 +++++++++++++++---- .../corelib/global/qnumeric/tst_qnumeric.cpp | 6 --- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/corelib/global/qnumeric.h b/src/corelib/global/qnumeric.h index bc2bbf9e53ea..723a462bae19 100644 --- a/src/corelib/global/qnumeric.h +++ b/src/corelib/global/qnumeric.h @@ -20,6 +20,18 @@ #include #include +#if __has_include() +// This was added by C23 and C++26, defining __STDC_VERSION_STDCKDINT_H__ when +// usable. Its provided functions are in the global namespace even in C++ (not +// std::) because they are sometimes macros. +# include +# if defined(Q_CC_GNU_ONLY) && (defined(Q_STL_LIBCPP) || Q_CC_GNU_ONLY < 1500) +// broken - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121811 +# else +# include +# endif +#endif + // min() and max() may be #defined by windows.h if that is included before, but we need them // for std::numeric_limits below. You should not use the min() and max() macros, so we just #undef. #ifdef min @@ -263,7 +275,10 @@ constexpr inline typename std::enable_if_t, bool> qAddOverflow(T v1, T v2, T *r) { -#if defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) + static_assert(!std::is_same_v, "Template must be an integral other than plain 'char'"); +#if defined(__STDC_VERSION_STDCKDINT_H__) + return ckd_add(r, v1, v2); +#elif defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) return __builtin_add_overflow(v1, v2, r); #else if (q20::is_constant_evaluated()) @@ -285,7 +300,7 @@ qAddOverflow(T v1, T v2, T *r) } # endif // defined(Q_HAVE_ADDCARRY) return QtPrivate::qAddOverflowGeneric(v1, v2, r); -#endif // defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) +#endif } template @@ -293,7 +308,10 @@ constexpr inline typename std::enable_if_t, bool> qAddOverflow(T v1, T v2, T *r) { -#if defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) + static_assert(!std::is_same_v, "Template must be an integral other than plain 'char'"); +#if defined(__STDC_VERSION_STDCKDINT_H__) + return ckd_add(r, v1, v2); +#elif defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) return __builtin_add_overflow(v1, v2, r); #else // Here's how we calculate the overflow: @@ -314,7 +332,7 @@ qAddOverflow(T v1, T v2, T *r) // (x ^ z) & (y ^ z) is negative if x and z have different signs // AND y and z have different signs return ((v1 ^ *r) & (v2 ^ *r)) < 0; -#endif // defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) +#endif } template @@ -322,13 +340,16 @@ constexpr inline typename std::enable_if_t, bool> qSubOverflow(T v1, T v2, T *r) { -#if defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) + static_assert(!std::is_same_v, "Template must be an integral other than plain 'char'"); +#if defined(__STDC_VERSION_STDCKDINT_H__) + return ckd_sub(r, v1, v2); +#elif defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) return __builtin_sub_overflow(v1, v2, r); #else // unsigned subtractions are well-defined *r = v1 - v2; return v1 < v2; -#endif // defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) +#endif } template @@ -336,7 +357,10 @@ constexpr inline typename std::enable_if_t, bool> qSubOverflow(T v1, T v2, T *r) { -#if defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) + static_assert(!std::is_same_v, "Template must be an integral other than plain 'char'"); +#if defined(__STDC_VERSION_STDCKDINT_H__) + return ckd_sub(r, v1, v2); +#elif defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) return __builtin_sub_overflow(v1, v2, r); #else // See above for explanation. This is the same with some signs reversed. @@ -347,7 +371,7 @@ qSubOverflow(T v1, T v2, T *r) *r = T(U(v1) - U(v2)); return ((v1 ^ *r) & (~v2 ^ *r)) < 0; -#endif // defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) +#endif } template @@ -355,7 +379,10 @@ constexpr inline typename std::enable_if_t || std::is_signed_v, bool> qMulOverflow(T v1, T v2, T *r) { -#if defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) + static_assert(!std::is_same_v, "Template must be an integral other than plain 'char'"); +#if defined(__STDC_VERSION_STDCKDINT_H__) + return ckd_mul(r, v1, v2); +#elif defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) # if defined(Q_INTRINSIC_MUL_OVERFLOW64) return __builtin_mul_overflow(v1, v2, r); # else @@ -388,7 +415,7 @@ qMulOverflow(T v1, T v2, T *r) # endif // defined(Q_INTRINSIC_MUL_OVERFLOW64) return QtPrivate::qMulOverflowGeneric(v1, v2, r); -#endif // defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) +#endif } #undef Q_HAVE_ADDCARRY @@ -421,6 +448,8 @@ template constexpr bool qSubOverflow(T v1, T *r) template constexpr bool qMulOverflow(T v1, std::integral_constant, T *r) { + static_assert(!std::is_same_v, "Template must be an integral other than plain 'char'"); + // Runtime detection for anything smaller than or equal to a register // width, as most architectures' multiplication instructions actually // produce a result twice as wide as the input registers, allowing us to diff --git a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp index d33f343045b2..d2fee5124dbc 100644 --- a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp +++ b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp @@ -990,12 +990,6 @@ using ushort = unsigned short; using uint = unsigned int; using ulong = unsigned long; -#if CHAR_MAX == 127 // char is signed -SIGNED_TYPE_TEST(char, CHAR_MIN, CHAR_MAX) -#else -UNSIGNED_TYPE_TEST(char, CHAR_MAX) -#endif - SIGNED_TYPE_TEST(schar, SCHAR_MIN, SCHAR_MAX) UNSIGNED_TYPE_TEST(uchar, UCHAR_MAX) From 8332a866c9a5b052a12d8e1ead167f95a8bd3c30 Mon Sep 17 00:00:00 2001 From: Michael Weghorn Date: Mon, 29 Sep 2025 09:12:16 +0200 Subject: [PATCH 3/3] a11y: Drop unintended check for window type being non-zero Commit 789a2310c0d09cb15cc9ecd793843a5abb3586c8 ("Remove Qt::WindowType::Desktop in QAccessbileObject") presumably was supposed to remove the check for the window type not being Qt::Desktop altogether, not changing it to check that the the type is non-zero instead. While at it, also update the comment to no longer mention the desktop. This amends 789a2310c0d09cb15cc9ecd793843a5abb3586c8. Task-number: QTBUG-140514 Change-Id: I53527db83ac67d343567cdeaba8204a95195c20b Reviewed-by: Volker Hilsheimer --- src/gui/accessible/qaccessibleobject.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/accessible/qaccessibleobject.cpp b/src/gui/accessible/qaccessibleobject.cpp index 5c723cdb289c..9edb0dff4fd6 100644 --- a/src/gui/accessible/qaccessibleobject.cpp +++ b/src/gui/accessible/qaccessibleobject.cpp @@ -119,14 +119,14 @@ QWindow *QAccessibleApplication::window() const return nullptr; } -// all toplevel windows except popups and the desktop +// all toplevel windows except popups static QObjectList topLevelObjects() { QObjectList list; const QWindowList tlw(QGuiApplication::topLevelWindows()); for (int i = 0; i < tlw.size(); ++i) { QWindow *w = tlw.at(i); - if (w->type() != Qt::Popup && w->type()) { + if (w->type() != Qt::Popup) { if (QAccessibleInterface *root = w->accessibleRoot()) { if (root->object()) list.append(root->object());