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 <ch.ehrlicher@gmx.de>
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
This commit is contained in:
Volker Hilsheimer 2023-03-08 20:05:01 +01:00
parent ce5fb1e709
commit cf5d9e9eb5
4 changed files with 74 additions and 20 deletions

View File

@ -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

View File

@ -6918,6 +6918,30 @@ bool QWidget::isActiveWindow() const
return false;
}
/*!
\fn void QWidget::setTabOrder(std::initializer_list<QWidget *> 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.

View File

@ -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<QWidget *> 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<QWidget *> 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)

View File

@ -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<QWidget *> getFocusChain(QWidget *start, bool bForward)
{
QList<QWidget *> 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<QWidgetPrivate *>(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<QWidget *>({&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<QWidget *>({&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<QWidget *> getFocusChain(QWidget *start, bool bForward)
{
QList<QWidget *> 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<QWidgetPrivate *>(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;