Focus
-
Tab key received in
QWidget::event()
. -
event()
callsfocusNextPrevChild()
on itself. -
focusNextPrevChild()
callsparentWidget()→focusNextPrevChild
ifthis
is not a top level window (so it gets called repeatedly till a window is found). -
On the window widget there is no parent to find, so
focusNextPrevChild()
finds the next focusable children withfocusNextPrevChild_helper
. -
Focus is set on said widget via
QWidget::setFocus
.
First, a tab key press is processed on QWidget::event
:
case QEvent::KeyPress: { QKeyEvent *k = (QKeyEvent *)event; bool res = false; if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier? if (k->key() == Qt::Key_Backtab || (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier))) res = focusNextPrevChild(false); else if (k->key() == Qt::Key_Tab) res = focusNextPrevChild(true); if (res) break; } keyPressEvent(k);
This just calls focusNextPrevChild
on itself. That is, a widget controls what
to do to pass the focus, because that function can be reimplemented, and do
something special. The example that such function gives is a rich text browser
which has the focus, and receives a tab press. That should instead highlight the
first link, then the second, etc. Only when the last link in the browser is
selected, the tab key should move the focus forward.
The focusNextPrevChild
being called by the widget with the focus might seem
odd, and rightfully so. Normally we see the focus being passed to a sibling
widget, not to a child one. For example, the OK/Cancel buttons in a dialog need
to pass back and forth the focus with tab and backtab. But none have children,
and they are indeed, siblings. What gives? Why "child" in the name of the
function?
The first lines of the focusNextPrevChild
function tell us how that’s
possible:
bool QWidget::focusNextPrevChild(bool next) { QWidget* p = parentWidget(); bool isSubWindow = (windowType() == Qt::SubWindow); if (!isWindow() && !isSubWindow && p) return p->focusNextPrevChild(next);
So the typical widget doesn’t normally change the focus itself: it calls its parent to fulfill the request. And the parent could call the grandparent, and so on, till the a window is found, and that one top level widget actually changes the focus. Which is done in the next lines of the function (partially simplified):
bool wrappingOccurred = false; QWidget *w = QApplicationPrivate::focusNextPrevChild_helper(this, next, &wrappingOccurred); if (!w) return false;
Qt::FocusReason reason = next ? Qt::TabFocusReason : Qt::BacktabFocusReason;
/* If we are about to wrap the focus chain, give the platform * implementation a chance to alter the wrapping behavior. This is * especially needed when the window is embedded in a window created by * another process. */ if (wrappingOccurred) { // ... }
w->setFocus(reason); return true;
So the bulk of the decision on which widget is the right one is done by that
helper in QApplicationPrivate
(remember that this is called from QWidget
).
But the details of what happens is right there: it just calls w→setFocus
on
the wanted one. A reimplementation of focusNextPrevChild
just needs to do
that.
What to do when wrappingOccurred
is true is not important. As the comment
says, seems necessary for a fairly involved case of embedding a window in
another application.
Let’s now look at focusNextPrevChild_helper
, a helper function which seems
weird to exist on the QApplicationPrivate
. But the comment on top of it tells
us the alleged reason:
/*!internal * Helper function that returns the new focus widget, but does not set the focus reason. * Returns \nullptr if a new focus widget could not be found. * Shared with QGraphicsProxyWidgetPrivate::findFocusChild() */ QWidget *QApplicationPrivate::focusNextPrevChild_helper(QWidget *toplevel, bool next, bool *wrappingOccurred)
So the function is supposedly shared between widgets and the graphics view framework. The mentioned function in QGraphicsProxyWidgetPrivate, however, has this comment:
Some of the logic is shared with QApplicationPrivate::focusNextPrevChild_helper
And it actually doesn’t use focusNextPrevChild_helper
, so in practice, the
helper is not shared as claimed (as of Qt 5.15). The focusNextPrevChild_helper
is called from QWidget::focusNextPrevChild
and QApplication::setActiveWindow
instead.
But let’s finally check what it does:
QWidget *QApplicationPrivate::focusNextPrevChild_helper(QWidget *toplevel, bool next, bool *wrappingOccurred) { uint focus_flag = qt_tab_all_widgets() ? Qt::TabFocus : Qt::StrongFocus;
QWidget *f = toplevel->focusWidget(); if (!f) f = toplevel;
The first thing is deciding between TabFocus
and StrongFocus
. Later the
function will iterate through widgets, and will obviously skip the ones with
Qt::NoFocus
. But it will only consider the ones with the right focus flag,
where "right" is decided by qt_tab_all_widgets
, which is just a helper that
returns:
QGuiApplication::styleHints()->tabFocusBehavior() == Qt::TabFocusAllControls;
For simplicity, just consider focus_flag == Qt::TabFocus
, which will be used
later to find widgets compatible with this flag.
The next lines decide on which is the focused widget, and are equivalent to:
QWidget * const f = toplevel->focusWidget() ? toplevel->focusWidget() : toplevel;
Remember that QWidget::focusWidget
just is:
(…) the last child of this widget that setFocus had been called on. For top level widgets this is the widget that will get focus in case this window gets activated.
When a widget gets focus via setFocus
, the focus child gets set on their
parents.
The future of smart pointers in Qt API
The use of deleteLater
seems one of the hardest parts:
So obviously this:
std::unique_ptr<> ptr; ptr→deleteLater();
should show a warning. Thus, the first step is to annotate deleteLater with a optional deprecation.
Then two new functions need to be added:
static QObject::deleteLater(QObject *ptr)
which does not violate that invariant. (Unless you take a pointer out of a unique_ptr and pass it in.)
And a second variant: static QObject::deleteLater(std::unique_ptr<QObject> ptr)
which quite obviously also keeps the invariant.
I think I missed the function in my patch, but that’s how you could easily do it.
Also a problem with layouts:
I don’t think these lines above are correct.
QBoxLayout::addWidget does not take ownership. Widgets are reparented to their correct parent once you call QWidget::setLayout in the next line, and QLayout operates on QLayoutItems that hold a weak pointer to the widgets that the layout places.
Also, a problem with elements on the stack:
Same as before, in essence a stack object is equivalent to a unique_ptr object.
Thus:
There’s no problem with having a QObjects without a parent on the stack/in a unique_ptr.
A QObject with a parent on the stack/unique_ptr is double owned and thus a problem. The severity of that problem is arguably worse with a unique_ptr.
Where my patch improves this situation is that the object ctors taking a parent ptr are all marked with a optional deprecation, thus if you opt into that, you’ll get a warning for the second case even for the stack allocation.
And yes this contrary to very crafty code that allocates objects in the right order on the stack, but that has never been the recommend way to use Qt.
How qobject_cast
works
-
qobject_cast<QSpinBox*>(widget)
is just callingstatic_cast<T>(ObjType::staticMetaObject.cast(object))
. -
QMetaObject::cast(QObject *obj)
is just callingobj→metaObject()→inherits(this))
on the meta object of the wished class. -
QMetaObject::inherits
compares the meta object returned by theobj→metaObject()
call (on the object which is being cast), with the meta object of the class we are casting to. If the comparison fails, tries to go up the hierarchy tree (so a pointer toQWidget
can be cast toQFrame
if the object happens to be aQLabel
, for example).
If one wants to cast a widget to specifically one type of widget, but none of
the derived subclasses, one can just compare the addresses directly. For
example, to test if a widget is just a QWidget
but not an instance of any
derived class, one can do object→metaObject() == &QWidget::staticMetaObject
.
Translation and internationalization mistakes
Notes to self
Styles in Qt
Some old notes that I took when I was trying to understand better the style system.
-
Styles: classes that inherit the abstract base class QStyle
-
To customize an existing style, inherit QProxyStyle and override functions.
-
To create a fully custom style, inherit QCommonStyle.
-
The API of QStyle contains the functions that draw the widgets.
-
The styles create a QPalette that contains QBrushes to draw with.
-
QStyle draws graphical "elements": widgets or their parts, like a scroll bar.