diff options
-rw-r--r-- | src/widgets/kernel/qwidget.h | 17 | ||||
-rw-r--r-- | tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp | 67 | ||||
-rw-r--r-- | tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp | 90 |
3 files changed, 150 insertions, 24 deletions
diff --git a/src/widgets/kernel/qwidget.h b/src/widgets/kernel/qwidget.h index ee6e7d5fe6..6c30e018df 100644 --- a/src/widgets/kernel/qwidget.h +++ b/src/widgets/kernel/qwidget.h @@ -211,11 +211,28 @@ class Q_WIDGETS_EXPORT QWidget : public QObject, public QPaintDevice Q_PROPERTY(QString windowFilePath READ windowFilePath WRITE setWindowFilePath) Q_PROPERTY(Qt::InputMethodHints inputMethodHints READ inputMethodHints WRITE setInputMethodHints) +#if 0 + // ### TODO: make this work (requires SFINAE-friendly connect()) template <typename...Args> using compatible_action_slot_args = std::void_t< decltype(QObject::connect(std::declval<QAction*>(), &QAction::triggered, std::declval<Args>()...)) >; +#else + // good-enough compromise for now + template <typename...Args> + using compatible_action_slot_args = std::enable_if_t<std::conjunction_v< +#if QT_CONFIG(shortcut) + std::disjunction< + std::is_same<Args, Qt::ConnectionType>, + std::negation<std::is_convertible<Args, QKeySequence>> + >..., +#endif + std::negation<std::is_convertible<Args, QIcon>>..., + std::negation<std::is_convertible<Args, const char*>>..., + std::negation<std::is_convertible<Args, QString>>... + >>; +#endif public: enum RenderFlag { DrawWindowBackground = 0x1, diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index 7ca08993ab..4fa568228a 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -160,6 +160,7 @@ public slots: void initTestCase(); void cleanup(); private slots: + void addActionOverloads(); void getSetCheck(); void fontPropagation(); void fontPropagation2(); @@ -644,6 +645,72 @@ void tst_QWidget::cleanup() QTRY_VERIFY(QApplication::topLevelWidgets().isEmpty()); } +template <typename T> +struct ImplicitlyConvertibleTo { + T t; + operator const T() const { return t; } + operator T() { return t; } +}; + +void testFunction0() {} +void testFunction1(bool) {} + +void tst_QWidget::addActionOverloads() +{ + // almost exhaustive check of addAction() overloads: + // (text), (icon, text), (icon, text, shortcut), (text, shortcut) + // each with a good sample of ways to QObject::connect() to + // QAction::triggered(bool) + QWidget w; + + // don't just pass QString etc - that'd be too easy (think QStringBuilder) + ImplicitlyConvertibleTo<QString> text = {QStringLiteral("foo")}; + ImplicitlyConvertibleTo<QIcon> icon; + + const auto check = [&](auto &...args) { // don't need to perfectly-forward, only lvalues passed + w.addAction(args...); + + w.addAction(args..., &w, SLOT(deleteLater())); + w.addAction(args..., &w, &QObject::deleteLater); + w.addAction(args..., testFunction0); + w.addAction(args..., &w, testFunction0); + w.addAction(args..., testFunction1); + w.addAction(args..., &w, testFunction1); + w.addAction(args..., [&](bool b) { w.setEnabled(b); }); + w.addAction(args..., &w, [&](bool b) { w.setEnabled(b); }); + + w.addAction(args..., &w, SLOT(deleteLater()), Qt::QueuedConnection); + w.addAction(args..., &w, &QObject::deleteLater, Qt::QueuedConnection); + // doesn't exist: w.addAction(args..., testFunction0, Qt::QueuedConnection); + w.addAction(args..., &w, testFunction0, Qt::QueuedConnection); + // doesn't exist: w.addAction(args..., testFunction1, Qt::QueuedConnection); + w.addAction(args..., &w, testFunction1, Qt::QueuedConnection); + // doesn't exist: w.addAction(args..., [&](bool b) { w.setEnabled(b); }, Qt::QueuedConnection); + w.addAction(args..., &w, [&](bool b) { w.setEnabled(b); }, Qt::QueuedConnection); + }; + const auto check1 = [&](auto &arg, auto &...args) { + check(arg, args...); + check(std::as_const(arg), args...); + }; + const auto check2 = [&](auto &arg1, auto &arg2, auto &...args) { + check1(arg1, arg2, args...); + check1(arg1, std::as_const(arg2), args...); + }; + [[maybe_unused]] + const auto check3 = [&](auto &arg1, auto &arg2, auto &arg3) { + check2(arg1, arg2, arg3); + check2(arg1, arg2, std::as_const(arg3)); + }; + + check1(text); + check2(icon, text); +#ifndef QT_NO_SHORTCUT + ImplicitlyConvertibleTo<QKeySequence> keySequence = {Qt::CTRL | Qt::Key_C}; + check2(text, keySequence); + check3(icon, text, keySequence); +#endif +} + void tst_QWidget::fontPropagation() { QScopedPointer<QWidget> testWidget(new QWidget); diff --git a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp index 55345f2ae5..0dc4c07069 100644 --- a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp +++ b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp @@ -287,44 +287,86 @@ void tst_QMenu::addActionsAndClear() QCOMPARE(menus[0]->actions().count(), 0); } -static void testFunction() { } +static void testFunction0() {} +static void testFunction1(bool) {} + +template <typename T> +struct ImplicitlyConvertibleTo { + T t; + operator const T() const { return t; } + operator T() { return t; } +}; void tst_QMenu::addActionsConnect() { + // almost exhaustive check of addAction() overloads: + // (text), (icon, text), (icon, text, shortcut), (text, shortcut) + // each with a good sample of ways to QObject::connect() to + // QAction::triggered(bool) QMenu menu; - const QString text = QLatin1String("bla"); - const QIcon icon; - menu.addAction(text, &menu, SLOT(deleteLater())); - menu.addAction(text, &menu, &QMenu::deleteLater); - menu.addAction(text, testFunction); - menu.addAction(text, &menu, testFunction); - menu.addAction(icon, text, &menu, SLOT(deleteLater())); - menu.addAction(icon, text, &menu, &QMenu::deleteLater); - menu.addAction(icon, text, testFunction); - menu.addAction(icon, text, &menu, testFunction); + + // don't just pass QString etc - that'd be too easy (think QStringBuilder) + ImplicitlyConvertibleTo<QString> text = {QLatin1String("bla")}; + ImplicitlyConvertibleTo<QIcon> icon; + + const auto check = [&](auto &...args) { // don't need to perfectly-forward, only lvalues passed + menu.addAction(args...); + + menu.addAction(args..., &menu, SLOT(deleteLater())); + menu.addAction(args..., &menu, &QMenu::deleteLater); + menu.addAction(args..., testFunction0); + menu.addAction(args..., &menu, testFunction0); + menu.addAction(args..., testFunction1); + menu.addAction(args..., &menu, testFunction1); + menu.addAction(args..., [&](bool b) { menu.setEnabled(b); }); + menu.addAction(args..., &menu, [&](bool b) { menu.setEnabled(b); }); + + menu.addAction(args..., &menu, SLOT(deleteLater()), Qt::QueuedConnection); + menu.addAction(args..., &menu, &QMenu::deleteLater, Qt::QueuedConnection); + // doesn't exist: menu.addAction(args..., testFunction0, Qt::QueuedConnection); + menu.addAction(args..., &menu, testFunction0, Qt::QueuedConnection); + // doesn't exist: menu.addAction(args..., testFunction1, Qt::QueuedConnection); + menu.addAction(args..., &menu, testFunction1, Qt::QueuedConnection); + // doesn't exist: menu.addAction(args..., [&](bool b) { menu.setEnabled(b); }, Qt::QueuedConnection); + menu.addAction(args..., &menu, [&](bool b) { menu.setEnabled(b); }, Qt::QueuedConnection); + }; + const auto check1 = [&](auto &arg, auto &...args) { + check(arg, args...); + check(std::as_const(arg), args...); + }; + const auto check2 = [&](auto &arg1, auto &arg2, auto &...args) { + check1(arg1, arg2, args...); + check1(arg1, std::as_const(arg2), args...); + }; + [[maybe_unused]] + const auto check3 = [&](auto &arg1, auto &arg2, auto &arg3) { + check2(arg1, arg2, arg3); + check2(arg1, arg2, std::as_const(arg3)); + }; + + check1(text); + check2(icon, text); #ifndef QT_NO_SHORTCUT - const QKeySequence keySequence(Qt::CTRL | Qt::Key_C); + ImplicitlyConvertibleTo<QKeySequence> keySequence = {Qt::CTRL | Qt::Key_C}; + check2(text, keySequence); + check3(icon, text, keySequence); #if QT_DEPRECATED_SINCE(6, 4) QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED menu.addAction(text, &menu, SLOT(deleteLater()), keySequence); menu.addAction(text, &menu, &QMenu::deleteLater, keySequence); - menu.addAction(text, testFunction, keySequence); - menu.addAction(text, &menu, testFunction, keySequence); + menu.addAction(text, testFunction0, keySequence); + menu.addAction(text, &menu, testFunction0, keySequence); + menu.addAction(text, testFunction1, keySequence); + menu.addAction(text, &menu, testFunction1, keySequence); menu.addAction(icon, text, &menu, SLOT(deleteLater()), keySequence); menu.addAction(icon, text, &menu, &QMenu::deleteLater, keySequence); - menu.addAction(icon, text, testFunction, keySequence); - menu.addAction(icon, text, &menu, testFunction, keySequence); + menu.addAction(icon, text, testFunction0, keySequence); + menu.addAction(icon, text, &menu, testFunction0, keySequence); + menu.addAction(icon, text, testFunction1, keySequence); + menu.addAction(icon, text, &menu, testFunction1, keySequence); QT_WARNING_POP #endif - menu.addAction(text, keySequence, &menu, SLOT(deleteLater())); - menu.addAction(text, keySequence, &menu, &QMenu::deleteLater); - menu.addAction(text, keySequence, testFunction); - menu.addAction(text, keySequence, &menu, testFunction); - menu.addAction(icon, text, keySequence, &menu, SLOT(deleteLater())); - menu.addAction(icon, text, keySequence, &menu, &QMenu::deleteLater); - menu.addAction(icon, text, keySequence, testFunction); - menu.addAction(icon, text, keySequence, &menu, testFunction); #endif // !QT_NO_SHORTCUT } |