From cf5d9e9eb5c4ad77f68aa2787bfb81550ded9c23 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Wed, 8 Mar 2023 20:05:01 +0100 Subject: [PATCH] QWidget: add overload to set tab order as a list of widgets The "two widgets at a time" API to set the tab order is awkward and easily misused (as the documentation explicitly explains). Add an inline overload that takes an initializer_list, and call the existing function for each consecutive pair of widgets in the list. Add documentation with snippet, and a test. Change-Id: I8e6f14a242866e3ee7cfb8ecade4697d6bdfb4d4 Reviewed-by: Christian Ehrlicher Reviewed-by: Richard Moe Gustavsen --- .../snippets/code/src_gui_kernel_qwidget.cpp | 5 ++ src/widgets/kernel/qwidget.cpp | 24 +++++++++ src/widgets/kernel/qwidget.h | 13 +++++ .../widgets/kernel/qwidget/tst_qwidget.cpp | 52 ++++++++++++------- 4 files changed, 74 insertions(+), 20 deletions(-) diff --git a/src/widgets/doc/snippets/code/src_gui_kernel_qwidget.cpp b/src/widgets/doc/snippets/code/src_gui_kernel_qwidget.cpp index 8f0c2968738..603ce14f80b 100644 --- a/src/widgets/doc/snippets/code/src_gui_kernel_qwidget.cpp +++ b/src/widgets/doc/snippets/code/src_gui_kernel_qwidget.cpp @@ -61,6 +61,11 @@ setTabOrder(c, d); // a to b to c to d //! [9] +//! [9.list] +setTabOrder({a, b, c, d}); // a to b to c to d +//! [9.list] + + //! [10] // WRONG setTabOrder(c, d); // c to d diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index cb432c84bc8..cde0542eee4 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -6918,6 +6918,30 @@ bool QWidget::isActiveWindow() const return false; } +/*! + \fn void QWidget::setTabOrder(std::initializer_list widgets) + \overload + \since 6.6 + + Sets the tab order for the widgets in the \a widgets list by calling + \l{QWidget::setTabOrder(QWidget *, QWidget *)} for each consecutive + pair of widgets. + + Instead of setting up each pair manually like this: + + \snippet code/src_gui_kernel_qwidget.cpp 9 + + you can call: + + \snippet code/src_gui_kernel_qwidget.cpp 9.list + + The call does not create a closed tab focus loop. If there are more widgets + with \l{Qt::TabFocus} focus policy, tabbing on \c{d} will move focus to one + of those widgets, not back to \c{a}. + + \sa setFocusPolicy(), setFocusProxy(), {Keyboard Focus in Widgets} +*/ + /*! Puts the \a second widget after the \a first widget in the focus order. diff --git a/src/widgets/kernel/qwidget.h b/src/widgets/kernel/qwidget.h index a0a1ca53005..a17803cabc3 100644 --- a/src/widgets/kernel/qwidget.h +++ b/src/widgets/kernel/qwidget.h @@ -432,6 +432,7 @@ public: void setFocusPolicy(Qt::FocusPolicy policy); bool hasFocus() const; static void setTabOrder(QWidget *, QWidget *); + static inline void setTabOrder(std::initializer_list widgets); void setFocusProxy(QWidget *); QWidget *focusProxy() const; Qt::ContextMenuPolicy contextMenuPolicy() const; @@ -916,6 +917,18 @@ inline bool QWidget::testAttribute(Qt::WidgetAttribute attribute) const return testAttribute_helper(attribute); } +inline void QWidget::setTabOrder(std::initializer_list widgets) +{ + QWidget *prev = nullptr; + for (const auto &widget : widgets) { + if (!prev) { + prev = widget; + } else { + QWidget::setTabOrder(prev, widget); + prev = widget; + } + } +} #define QWIDGETSIZE_MAX ((1<<24)-1) diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index 69b385f95b9..8e497bbb30d 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -173,6 +173,7 @@ private slots: void appFocusWidgetWhenLosingFocusProxy(); void explicitTabOrderWithComplexWidget(); void explicitTabOrderWithSpinBox_QTBUG81097(); + void tabOrderList(); #if defined(Q_OS_WIN) void activation(); #endif @@ -1929,6 +1930,26 @@ public: QLineEdit *lineEdit3; }; +static QList getFocusChain(QWidget *start, bool bForward) +{ + QList ret; + QWidget *cur = start; + // detect infinite loop + int count = 100; + auto loopGuard = qScopeGuard([]{ + QFAIL("Inifinite loop detected in focus chain"); + }); + do { + ret += cur; + auto widgetPrivate = static_cast(qt_widget_private(cur)); + cur = bForward ? widgetPrivate->focus_next : widgetPrivate->focus_prev; + if (!--count) + return ret; + } while (cur != start); + loopGuard.dismiss(); + return ret; +} + void tst_QWidget::defaultTabOrder() { if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) @@ -2048,6 +2069,17 @@ void tst_QWidget::reverseTabOrder() QVERIFY(firstEdit->hasFocus()); } +void tst_QWidget::tabOrderList() +{ + Composite c; + QCOMPARE(getFocusChain(&c, true), + QList({&c, c.lineEdit1, c.lineEdit2, c.lineEdit3})); + QWidget::setTabOrder({c.lineEdit3, c.lineEdit2, c.lineEdit1}); + // not starting with 3 like one would maybe expect, but still 3, 2, 1 + QCOMPARE(getFocusChain(&c, true), + QList({&c, c.lineEdit1, c.lineEdit3, c.lineEdit2})); +} + void tst_QWidget::tabOrderWithProxy() { if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) @@ -2269,26 +2301,6 @@ void tst_QWidget::tabOrderWithCompoundWidgets() QVERIFY(lastEdit->hasFocus()); } -static QList getFocusChain(QWidget *start, bool bForward) -{ - QList ret; - QWidget *cur = start; - // detect infinite loop - int count = 100; - auto loopGuard = qScopeGuard([]{ - QFAIL("Inifinite loop detected in focus chain"); - }); - do { - ret += cur; - auto widgetPrivate = static_cast(qt_widget_private(cur)); - cur = bForward ? widgetPrivate->focus_next : widgetPrivate->focus_prev; - if (!--count) - return ret; - } while (cur != start); - loopGuard.dismiss(); - return ret; -} - void tst_QWidget::tabOrderWithProxyOutOfOrder() { Container container;