1.void setFocus()
inline void setFocus() { setFocus(Qt::OtherFocusReason); }
void QWidget::setFocus(Qt::FocusReason reason)
{
if (!isEnabled())
return;
QWidget *f = d_func()->deepestFocusProxy();
if (!f)
f = this;
if (QApplication::focusWidget() == f)
return;
#if QT_CONFIG(graphicsview)
QWidget *previousProxyFocus = nullptr;
if (const auto &topData = window()->d_func()->extra) {
if (topData->proxyWidget && topData->proxyWidget->hasFocus()) {
previousProxyFocus = topData->proxyWidget->widget()->focusWidget();
if (previousProxyFocus && previousProxyFocus->focusProxy())
previousProxyFocus = previousProxyFocus->focusProxy();
if (previousProxyFocus == f && !topData->proxyWidget->d_func()->proxyIsGivingFocus)
return;
}
}
#endif
#if QT_CONFIG(graphicsview)
// Update proxy state
if (const auto &topData = window()->d_func()->extra) {
if (topData->proxyWidget && !topData->proxyWidget->hasFocus()) {
f->d_func()->updateFocusChild();
topData->proxyWidget->d_func()->focusFromWidgetToProxy = 1;
topData->proxyWidget->setFocus(reason);
topData->proxyWidget->d_func()->focusFromWidgetToProxy = 0;
}
}
#endif
if (f->isActiveWindow()) {
QWidget *prev = QApplicationPrivate::focus_widget;
if (prev) {
if (reason != Qt::PopupFocusReason && reason != Qt::MenuBarFocusReason
&& prev->testAttribute(Qt::WA_InputMethodEnabled)) {
QGuiApplication::inputMethod()->commit();
}
if (reason != Qt::NoFocusReason) {
QFocusEvent focusAboutToChange(QEvent::FocusAboutToChange, reason);
QCoreApplication::sendEvent(prev, &focusAboutToChange);
}
}
f->d_func()->updateFocusChild();
QApplicationPrivate::setFocusWidget(f, reason);
#ifndef QT_NO_ACCESSIBILITY
// menus update the focus manually and this would create bogus events
if (!(f->inherits("QMenuBar") || f->inherits("QMenu") || f->inherits("QMenuItem")))
{
QAccessibleEvent event(f, QAccessible::Focus);
QAccessible::updateAccessibility(&event);
}
#endif
#if QT_CONFIG(graphicsview)
if (const auto &topData = window()->d_func()->extra) {
if (topData->proxyWidget) {
if (previousProxyFocus && previousProxyFocus != f) {
// Send event to self
QFocusEvent event(QEvent::FocusOut, reason);
QPointer<QWidget> that = previousProxyFocus;
QCoreApplication::sendEvent(previousProxyFocus, &event);
if (that)
QCoreApplication::sendEvent(that->style(), &event);
}
if (!isHidden()) {
#if QT_CONFIG(graphicsview)
// Update proxy state
if (const auto &topData = window()->d_func()->extra)
if (topData->proxyWidget && topData->proxyWidget->hasFocus())
topData->proxyWidget->d_func()->updateProxyInputMethodAcceptanceFromWidget();
#endif
// Send event to self
QFocusEvent event(QEvent::FocusIn, reason);
QPointer<QWidget> that = f;
QCoreApplication::sendEvent(f, &event);
if (that)
QCoreApplication::sendEvent(that->style(), &event);
}
}
}
#endif
} else {
f->d_func()->updateFocusChild();
}
}
首先,将焦点即将更改事件发送到当前获得焦点的QWidget以告诉它即将失去焦点。 然后焦点改变,focus out 事件被发送到当前的焦点项,并发送一个 focus in 事件到新焦点项告诉它刚刚获得焦点。
2.void clearFocus();
void QWidget::clearFocus()
{
if (hasFocus()) {
if (testAttribute(Qt::WA_InputMethodEnabled))
QGuiApplication::inputMethod()->commit();
QFocusEvent focusAboutToChange(QEvent::FocusAboutToChange);
QCoreApplication::sendEvent(this, &focusAboutToChange);
}
QTLWExtra *extra = window()->d_func()->maybeTopData();
QObject *originalFocusObject = (extra && extra->window) ? extra->window->focusObject() : nullptr;
QWidget *w = this;
while (w) {
// Just like setFocus(), we update (clear) the focus_child of our parents
if (w->d_func()->focus_child == this)
w->d_func()->focus_child = nullptr;
w = w->parentWidget();
}
// We've potentially cleared the focus_child of our parents, so we need
// to report this to the rest of Qt. Note that the focus_child is not the same
// thing as the application's focusWidget, which is why this piece of code is
// not inside a hasFocus() block.
if (originalFocusObject && originalFocusObject != extra->window->focusObject())
emit extra->window->focusObjectChanged(extra->window->focusObject());
#if QT_CONFIG(graphicsview)
const auto &topData = d_func()->extra;
if (topData && topData->proxyWidget)
topData->proxyWidget->clearFocus();
#endif
if (hasFocus()) {
// Update proxy state
QApplicationPrivate::setFocusWidget(nullptr, Qt::OtherFocusReason);
#ifndef QT_NO_ACCESSIBILITY
QAccessibleEvent event(this, QAccessible::Focus);
QAccessible::updateAccessibility(&event);
#endif
}
}
先发送focusAboutToChange事件到当前焦点所在的QWidget, 然后,将父控件的focus_child置nullptr.
3.static void setTabOrder(QWidget *, QWidget *);
在焦点顺序中将第二个QWidget放在第一个QWidget之后。
void QWidget::setTabOrder(QWidget* first, QWidget *second)
{
if (!first || !second || first == second
|| first->focusPolicy() == Qt::NoFocus
|| second->focusPolicy() == Qt::NoFocus)
return;
if (Q_UNLIKELY(first->window() != second->window())) {
qWarning("QWidget::setTabOrder: 'first' and 'second' must be in the same window");
return;
}
auto determineLastFocusChild = [](QWidget *target, QWidget *&lastFocusChild)
{
// Since we need to repeat the same logic for both 'first' and 'second', we add a function that
// determines the last focus child for a widget, taking proxies and compound widgets into account.
// If the target is not a compound widget (it doesn't have a focus proxy that points to a child),
// 'lastFocusChild' will be set to the target itself.
lastFocusChild = target;
QWidget *focusProxy = target->d_func()->deepestFocusProxy();
if (!focusProxy || !target->isAncestorOf(focusProxy)) {
// QTBUG-81097: Another case is possible here. We can have a child
// widget, that sets its focusProxy() to the parent (target).
// An example of such widget is a QLineEdit, nested into
// a QAbstractSpinBox. In this case such widget should be considered
// the last focus child.
for (auto *object : target->children()) {
QWidget *w = qobject_cast<QWidget*>(object);
if (w && w->focusProxy() == target) {
lastFocusChild = w;
break;
}
}
return;
}
lastFocusChild = focusProxy;
for (QWidget *focusNext = lastFocusChild->d_func()->focus_next;
focusNext != focusProxy && target->isAncestorOf(focusNext) && focusNext->window() == focusProxy->window();
focusNext = focusNext->d_func()->focus_next) {
if (focusNext->focusPolicy() != Qt::NoFocus)
lastFocusChild = focusNext;
}
};
auto setPrev = [](QWidget *w, QWidget *prev)
{
w->d_func()->focus_prev = prev;
};
auto setNext = [](QWidget *w, QWidget *next)
{
w->d_func()->focus_next = next;
};
// remove the second widget from the chain
QWidget *lastFocusChildOfSecond;
determineLastFocusChild(second, lastFocusChildOfSecond);
{
QWidget *oldPrev = second->d_func()->focus_prev;
QWidget *prevWithFocus = oldPrev;
while (prevWithFocus->focusPolicy() == Qt::NoFocus)
prevWithFocus = prevWithFocus->d_func()->focus_prev;
// only widgets between first and second -> all is fine
if (prevWithFocus == first)
return;
QWidget *oldNext = lastFocusChildOfSecond->d_func()->focus_next;
setPrev(oldNext, oldPrev);
setNext(oldPrev, oldNext);
}
// insert the second widget into the chain
QWidget *lastFocusChildOfFirst;
determineLastFocusChild(first, lastFocusChildOfFirst);
{
QWidget *oldNext = lastFocusChildOfFirst->d_func()->focus_next;
setPrev(second, lastFocusChildOfFirst);
setNext(lastFocusChildOfFirst, second);
setPrev(oldNext, lastFocusChildOfSecond);
setNext(lastFocusChildOfSecond, oldNext);
}
}
4. void setFocusProxy(QWidget *);
void QWidget::setFocusProxy(QWidget * w)
{
Q_D(QWidget);
if (!w && !d->extra)
return;
for (QWidget* fp = w; fp; fp = fp->focusProxy()) {
if (Q_UNLIKELY(fp == this)) {
qWarning("QWidget: %s (%s) already in focus proxy chain", metaObject()->className(), objectName().toLocal8Bit().constData());
return;
}
}
const bool moveFocusToProxy = (QApplicationPrivate::focus_widget == this);
d->createExtra();
d->extra->focus_proxy = w;
if (moveFocusToProxy)
setFocus(Qt::OtherFocusReason);
}
一些QWidget可以“拥有焦点”,但会创建一个子QWidget,例如QLineEdit,来实际处理焦点。 在这种情况下,QWidget可以将QLineEdit设置为其焦点代理。
5.bool focusNextChild()
inline bool focusNextChild() { return focusNextPrevChild(true); }
bool QWidget::focusNextPrevChild(bool next)
{
QWidget* p = parentWidget();
bool isSubWindow = (windowType() == Qt::SubWindow);
if (!isWindow() && !isSubWindow && p)
return p->focusNextPrevChild(next);
#if QT_CONFIG(graphicsview)
Q_D(QWidget);
if (d->extra && d->extra->proxyWidget)
return d->extra->proxyWidget->focusNextPrevChild(next);
#endif
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) {
QWindow *window = windowHandle();
if (window != nullptr) {
QWindowPrivate *winp = qt_window_private(window);
if (winp->platformWindow != nullptr) {
QFocusEvent event(QEvent::FocusIn, reason);
event.ignore();
winp->platformWindow->windowEvent(&event);
if (event.isAccepted()) return true;
}
}
}
w->setFocus(reason);
return true;
}
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;
QWidget *w = f;
QWidget *test = f->d_func()->focus_next;
bool seenWindow = false;
bool focusWidgetAfterWindow = false;
while (test && test != f) {
if (test->isWindow())
seenWindow = true;
// If the next focus widget has a focus proxy, we need to check to ensure
// that the proxy is in the correct parent-child direction (according to
// \a next). This is to ensure that we can tab in and out of compound widgets
// without getting stuck in a tab-loop between parent and child.
QWidget *focusProxy = test->d_func()->deepestFocusProxy();
const bool canTakeFocus = ((focusProxy ? focusProxy->focusPolicy() : test->focusPolicy())
& focus_flag) == focus_flag;
const bool composites = focusProxy ? (next ? focusProxy->isAncestorOf(test)
: test->isAncestorOf(focusProxy))
: false;
if (canTakeFocus && !composites
&& test->isVisibleTo(toplevel) && test->isEnabled()
&& !(w->windowType() == Qt::SubWindow && !w->isAncestorOf(test))
&& (toplevel->windowType() != Qt::SubWindow || toplevel->isAncestorOf(test))
&& f != focusProxy) {
w = test;
if (seenWindow)
focusWidgetAfterWindow = true;
if (next)
break;
}
test = test->d_func()->focus_next;
}
if (wrappingOccurred != nullptr)
*wrappingOccurred = next ? focusWidgetAfterWindow : !focusWidgetAfterWindow;
if (w == f) {
if (qt_in_tab_key_event) {
w->window()->setAttribute(Qt::WA_KeyboardFocusChange);
w->update();
}
return nullptr;
}
return w;
}
找到一个新的QWidget以赋予键盘焦点,对于 Tab,如果可以找到则返回 true,如果不能,则返回 false。
在focusNextPrevChild_helper函数中while循环遍历查找是否有可获得焦点的控件。
6.bool focusPreviousChild()
inline bool focusPreviousChild() { return focusNextPrevChild(false); }
focusNextPrevChild_helper函数参数为false。