summaryrefslogtreecommitdiffstats
path: root/src/widgets/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets/kernel')
-rw-r--r--src/widgets/kernel/qapplication.cpp6
-rw-r--r--src/widgets/kernel/qgesturemanager.cpp3
-rw-r--r--src/widgets/kernel/qlayout.cpp5
-rw-r--r--src/widgets/kernel/qwidget.cpp583
-rw-r--r--src/widgets/kernel/qwidget_p.h35
-rw-r--r--src/widgets/kernel/qwidgetrepaintmanager.cpp6
-rw-r--r--src/widgets/kernel/qwidgetwindow.cpp33
-rw-r--r--src/widgets/kernel/qwindowcontainer.cpp54
-rw-r--r--src/widgets/kernel/qwindowcontainer_p.h4
9 files changed, 552 insertions, 177 deletions
diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp
index de7d93ce20..a1392e10dc 100644
--- a/src/widgets/kernel/qapplication.cpp
+++ b/src/widgets/kernel/qapplication.cpp
@@ -1094,6 +1094,12 @@ QPalette QApplicationPrivate::basePalette() const
if (const QPalette *themePalette = platformTheme() ? platformTheme()->palette() : nullptr)
palette = themePalette->resolve(palette);
+ // This palette now is Qt-generated, so reset the resolve mask. This allows
+ // QStyle::polish implementations to respect palettes that are user provided,
+ // by checking if the palette has a brush set for a color that the style might
+ // otherwise overwrite.
+ palette.setResolveMask(0);
+
// Finish off by letting the application style polish the palette. This will
// not result in the polished palette becoming a user-set palette, as the
// resulting base palette is only used as a fallback, with the resolve mask
diff --git a/src/widgets/kernel/qgesturemanager.cpp b/src/widgets/kernel/qgesturemanager.cpp
index c93876c500..edb159bbbf 100644
--- a/src/widgets/kernel/qgesturemanager.cpp
+++ b/src/widgets/kernel/qgesturemanager.cpp
@@ -610,7 +610,8 @@ void QGestureManager::deliverEvents(const QSet<QGesture *> &gestures,
QWidget *child = topLevel->childAt(topLevel->mapFromGlobal(pt));
target = child ? child : topLevel;
}
- } else {
+ }
+ if (!target) {
// or use the context of the gesture
QObject *context = m_gestureOwners.value(gesture, 0);
if (context->isWidgetType())
diff --git a/src/widgets/kernel/qlayout.cpp b/src/widgets/kernel/qlayout.cpp
index 0251ecd7fd..a826ea75bc 100644
--- a/src/widgets/kernel/qlayout.cpp
+++ b/src/widgets/kernel/qlayout.cpp
@@ -520,10 +520,11 @@ void QLayoutPrivate::doResize()
void QLayout::widgetEvent(QEvent *e)
{
Q_D(QLayout);
- if (!d->enabled)
+ const QEvent::Type type = e->type();
+ if (!d->enabled && type != QEvent::ChildRemoved)
return;
- switch (e->type()) {
+ switch (type) {
case QEvent::Resize:
if (d->activated)
d->doResize();
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp
index 30daee1b79..6eff4abfb0 100644
--- a/src/widgets/kernel/qwidget.cpp
+++ b/src/widgets/kernel/qwidget.cpp
@@ -6,6 +6,7 @@
#include "qapplication_p.h"
#include "qbrush.h"
#include "qcursor.h"
+#include "private/qduplicatetracker_p.h"
#include "qevent.h"
#include "qlayout.h"
#if QT_CONFIG(menu)
@@ -84,6 +85,7 @@ using namespace Qt::StringLiterals;
Q_LOGGING_CATEGORY(lcWidgetPainting, "qt.widgets.painting", QtWarningMsg);
Q_LOGGING_CATEGORY(lcWidgetShowHide, "qt.widgets.showhide", QtWarningMsg);
Q_LOGGING_CATEGORY(lcWidgetWindow, "qt.widgets.window", QtWarningMsg);
+Q_LOGGING_CATEGORY(lcWidgetFocus, "qt.widgets.focus")
#ifndef QT_NO_DEBUG_STREAM
namespace {
@@ -818,12 +820,7 @@ struct QWidgetExceptionCleaner
Q_UNUSED(d);
#else
QWidgetPrivate::allWidgets->remove(that);
- if (d->focus_next != that) {
- if (d->focus_next)
- d->focus_next->d_func()->focus_prev = d->focus_prev;
- if (d->focus_prev)
- d->focus_prev->d_func()->focus_next = d->focus_next;
- }
+ d->removeFromFocusChain();
#endif
}
};
@@ -991,7 +988,7 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f)
//give potential windows a bigger "pre-initial" size; create() will give them a new size later
data.crect = parentWidget ? QRect(0,0,100,30) : QRect(0,0,640,480);
- focus_next = focus_prev = q;
+ initFocusChain();
if ((f & Qt::WindowType_Mask) == Qt::Desktop)
q->create();
@@ -1292,7 +1289,7 @@ void QWidgetPrivate::create()
Qt::WindowFlags &flags = data.window_flags;
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
+#if defined(QT_PLATFORM_UIKIT)
if (q->testAttribute(Qt::WA_ContentsMarginsRespectsSafeArea))
flags |= Qt::MaximizeUsingFullscreenGeometryHint;
#endif
@@ -1477,17 +1474,9 @@ QWidget::~QWidget()
// delete layout while we still are a valid widget
delete d->layout;
d->layout = nullptr;
- // Remove myself from focus list
-
- Q_ASSERT(d->focus_next->d_func()->focus_prev == this);
- Q_ASSERT(d->focus_prev->d_func()->focus_next == this);
-
- if (d->focus_next != this) {
- d->focus_next->d_func()->focus_prev = d->focus_prev;
- d->focus_prev->d_func()->focus_next = d->focus_next;
- d->focus_next = d->focus_prev = nullptr;
- }
+ // Remove this from focus list
+ d->removeFromFocusChain(QWidgetPrivate::FocusChainRemovalRule::AssertConsistency);
QT_TRY {
#if QT_CONFIG(graphicsview)
@@ -5171,6 +5160,7 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset,
const QRegion oldSystemClip = enginePriv->systemClip;
const QRegion oldBaseClip = enginePriv->baseSystemClip;
const QRegion oldSystemViewport = enginePriv->systemViewport;
+ const Qt::LayoutDirection oldLayoutDirection = painter->layoutDirection();
// This ensures that all painting triggered by render() is clipped to the current engine clip.
if (painter->hasClipping()) {
@@ -5179,6 +5169,7 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset,
} else {
enginePriv->setSystemViewport(oldSystemClip);
}
+ painter->setLayoutDirection(layoutDirection());
d->render(target, targetOffset, toBePainted, renderFlags);
@@ -5186,6 +5177,7 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset,
enginePriv->baseSystemClip = oldBaseClip;
enginePriv->setSystemTransformAndViewport(oldTransform, oldSystemViewport);
enginePriv->systemStateChanged();
+ painter->setLayoutDirection(oldLayoutDirection);
// Restore shared painter.
d->setSharedPainter(oldPainter);
@@ -6406,39 +6398,18 @@ void QWidget::setFocusProxy(QWidget * w)
break;
}
Q_ASSERT(firstChild); // can't be nullptr since w is a child
- QWidget *oldNext = d->focus_next;
- QWidget *oldPrev = d->focus_prev;
- oldNext->d_func()->focus_prev = oldPrev;
- oldPrev->d_func()->focus_next = oldNext;
-
- oldPrev = firstChild->d_func()->focus_prev;
- d->focus_next = firstChild;
- d->focus_prev = oldPrev;
- oldPrev->d_func()->focus_next = this;
- firstChild->d_func()->focus_prev = this;
+ d->insertIntoFocusChainBefore(firstChild);
} else if (w && w->isAncestorOf(this)) {
// If the focus proxy is a parent, 'this' has to be inserted directly after its parent in the focus chain
// remove it from the chain and insert this into the focus chain after its parent
// is this the case already?
- QWidget *parentsNext = w->d_func()->focus_next;
+ QWidget *parentsNext = w->nextInFocusChain();
if (parentsNext == this) {
// nothing to do.
- Q_ASSERT(d->focus_prev == w);
+ Q_ASSERT(previousInFocusChain() == w);
} else {
- // Remove 'this' from the focus chain by making prev and next point directly to each other
- QWidget *myOldNext = d->focus_next;
- QWidget *myOldPrev = d->focus_prev;
- if (myOldNext && myOldPrev) {
- myOldNext->d_func()->focus_prev = myOldPrev;
- myOldPrev->d_func()->focus_next = myOldNext;
- }
-
- // Insert 'this' behind the parent
- w->d_func()->focus_next = this;
- d->focus_prev = w;
- d->focus_next = parentsNext;
- parentsNext->d_func()->focus_prev = this;
+ d->QWidgetPrivate::insertIntoFocusChainAfter(w);
}
}
@@ -6648,7 +6619,9 @@ void QWidgetPrivate::setFocus_sys()
{
Q_Q(QWidget);
// Embedded native widget may have taken the focus; get it back to toplevel
- // if that is the case (QTBUG-25852)
+ // if that is the case (QTBUG-25852), unless widget is a window container.
+ if (extra && extra->hasWindowContainer)
+ return;
// Do not activate in case the popup menu opens another application (QTBUG-70810)
// unless the application is embedded (QTBUG-71991).
if (QWindow *nativeWindow = q->testAttribute(Qt::WA_WState_Created) ? q->window()->windowHandle() : nullptr) {
@@ -6871,7 +6844,8 @@ QObject *QWidgetPrivate::focusObject()
*/
QWidget *QWidget::nextInFocusChain() const
{
- return const_cast<QWidget *>(d_func()->focus_next);
+ Q_D(const QWidget);
+ return d->nextPrevElementInFocusChain(QWidgetPrivate::FocusDirection::Next);
}
/*!
@@ -6884,7 +6858,8 @@ QWidget *QWidget::nextInFocusChain() const
*/
QWidget *QWidget::previousInFocusChain() const
{
- return const_cast<QWidget *>(d_func()->focus_prev);
+ Q_D(const QWidget);
+ return d->nextPrevElementInFocusChain(QWidgetPrivate::FocusDirection::Previous);
}
/*!
@@ -7039,9 +7014,9 @@ void QWidget::setTabOrder(QWidget* first, QWidget *second)
}
} else if (target->isAncestorOf(focusProxy)) {
lastFocusChild = focusProxy;
- for (QWidget *focusNext = lastFocusChild->d_func()->focus_next;
+ for (QWidget *focusNext = lastFocusChild->nextInFocusChain();
focusNext != focusProxy && target->isAncestorOf(focusNext) && focusNext->window() == focusProxy->window();
- focusNext = focusNext->d_func()->focus_next) {
+ focusNext = focusNext->nextInFocusChain()) {
if (focusNext == noFurtherThan)
break;
if (focusNext->focusPolicy() != Qt::NoFocus)
@@ -7050,13 +7025,6 @@ void QWidget::setTabOrder(QWidget* first, QWidget *second)
}
return lastFocusChild;
};
- auto setPrev = [](QWidget *w, QWidget *prev) {
- w->d_func()->focus_prev = prev;
- };
- auto setNext = [](QWidget *w, QWidget *next) {
- w->d_func()->focus_next = next;
- };
-
// detect inflection in case we have compound widgets
QWidget *lastFocusChildOfFirst = determineLastFocusChild(first, second);
if (lastFocusChildOfFirst == second)
@@ -7065,28 +7033,15 @@ void QWidget::setTabOrder(QWidget* first, QWidget *second)
if (lastFocusChildOfSecond == first)
lastFocusChildOfSecond = second;
- // remove the second widget from the chain
- {
- 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 *oldNext = lastFocusChildOfFirst->d_func()->focus_next;
- setPrev(second, lastFocusChildOfFirst);
- setNext(lastFocusChildOfFirst, second);
- setPrev(oldNext, lastFocusChildOfSecond);
- setNext(lastFocusChildOfSecond, oldNext);
- }
+ // Return if only NoFocus widgets are between first and second
+ QWidget *oldPrev = second->previousInFocusChain();
+ QWidget *prevWithFocus = oldPrev;
+ while (prevWithFocus->focusPolicy() == Qt::NoFocus)
+ prevWithFocus = prevWithFocus->previousInFocusChain();
+ if (prevWithFocus == first)
+ return;
+ const QWidgetList chain = QWidgetPrivate::takeFromFocusChain(second, lastFocusChildOfSecond);
+ QWidgetPrivate::insertIntoFocusChain(chain, QWidgetPrivate::FocusDirection::Next, lastFocusChildOfFirst);
}
void QWidget::setTabOrder(std::initializer_list<QWidget *> widgets)
@@ -7124,67 +7079,8 @@ void QWidgetPrivate::reparentFocusWidgets(QWidget * oldtlw)
if (focus_child)
focus_child->clearFocus();
- // separate the focus chain into new (children of myself) and old (the rest)
- QWidget *firstOld = nullptr;
- //QWidget *firstNew = q; //invariant
- QWidget *o = nullptr; // last in the old list
- QWidget *n = q; // last in the new list
-
- bool prevWasNew = true;
- QWidget *w = focus_next;
-
- //Note: for efficiency, we do not maintain the list invariant inside the loop
- //we append items to the relevant list, and we optimize by not changing pointers
- //when subsequent items are going into the same list.
- while (w != q) {
- bool currentIsNew = q->isAncestorOf(w);
- if (currentIsNew) {
- if (!prevWasNew) {
- //prev was old -- append to new list
- n->d_func()->focus_next = w;
- w->d_func()->focus_prev = n;
- }
- n = w;
- } else {
- if (prevWasNew) {
- //prev was new -- append to old list, if there is one
- if (o) {
- o->d_func()->focus_next = w;
- w->d_func()->focus_prev = o;
- } else {
- // "create" the old list
- firstOld = w;
- }
- }
- o = w;
- }
- w = w->d_func()->focus_next;
- prevWasNew = currentIsNew;
- }
-
- //repair the old list:
- if (firstOld) {
- o->d_func()->focus_next = firstOld;
- firstOld->d_func()->focus_prev = o;
- }
-
- if (!q->isWindow()) {
- QWidget *topLevel = q->window();
- //insert new chain into toplevel's chain
-
- QWidget *prev = topLevel->d_func()->focus_prev;
-
- topLevel->d_func()->focus_prev = n;
- prev->d_func()->focus_next = q;
-
- focus_prev = prev;
- n->d_func()->focus_next = topLevel;
- } else {
- //repair the new list
- n->d_func()->focus_next = q;
- focus_prev = n;
- }
-
+ insertIntoFocusChain(QWidgetPrivate::FocusDirection::Previous, q->window());
+ reparentFocusChildren(QWidgetPrivate::FocusDirection::Next);
}
/*!
@@ -10720,9 +10616,15 @@ void qSendWindowChangeToTextureChildrenRecursively(QWidget *widget, QEvent::Type
for (int i = 0; i < d->children.size(); ++i) {
QWidget *w = qobject_cast<QWidget *>(d->children.at(i));
- if (w && !w->isWindow() && QWidgetPrivate::get(w)->textureChildSeen)
+ if (w && !w->isWindow())
qSendWindowChangeToTextureChildrenRecursively(w, eventType);
}
+
+ // Notify QWidgetWindow after we've notified all child QWidgets
+ if (auto *window = d->windowHandle(QWidgetPrivate::WindowHandleMode::Direct)) {
+ QEvent e(eventType);
+ QCoreApplication::sendEvent(window, &e);
+ }
}
/*!
@@ -10753,6 +10655,7 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
const bool resized = testAttribute(Qt::WA_Resized);
const bool wasCreated = testAttribute(Qt::WA_WState_Created);
QWidget *oldtlw = window();
+ Q_ASSERT(oldtlw);
if (f & Qt::Window) // Frame geometry likely changes, refresh.
d->data.fstrut_dirty = true;
@@ -10795,7 +10698,7 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
// texture-based widgets need a pre-notification when their associated top-level window changes
// This is not under the wasCreated/newParent conditions above in order to also play nice with QDockWidget.
- if (d->textureChildSeen && ((!parent && parentWidget()) || (parent && parent->window() != oldtlw)))
+ if (oldtlw->d_func()->usesRhiFlush && ((!parent && parentWidget()) || (parent && parent->window() != oldtlw)))
qSendWindowChangeToTextureChildrenRecursively(this, QEvent::WindowAboutToChangeInternal);
// If we get parented into another window, children will be folded
@@ -10876,7 +10779,7 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
// texture-based widgets need another event when their top-level window
// changes (more precisely, has already changed at this point)
- if (d->textureChildSeen && oldtlw != window())
+ if (oldtlw->d_func()->usesRhiFlush && oldtlw != window())
qSendWindowChangeToTextureChildrenRecursively(this, QEvent::WindowChangeInternal);
if (!wasCreated) {
@@ -13371,6 +13274,404 @@ QDebug operator<<(QDebug debug, const QWidget *widget)
}
#endif // !QT_NO_DEBUG_STREAM
+
+// *************************** Focus abstraction ************************************
+
+#define FOCUS_NEXT(w) w->d_func()->focus_next
+#define FOCUS_PREV(w) w->d_func()->focus_prev
+
+/*!
+ \internal
+ \return next or previous element in the focus chain, depending on
+ \param direction, irrespective of focus proxies or widgets with Qt::NoFocus.
+ */
+QWidget *QWidgetPrivate::nextPrevElementInFocusChain(FocusDirection direction) const
+{
+ Q_Q(const QWidget);
+ return direction == FocusDirection::Next ? FOCUS_NEXT(q) : FOCUS_PREV(q);
+}
+
+/*!
+ \internal
+ Removes a widget from the focus chain, respecting the flags set in \param rules.
+ \list
+ \li EnsureFocusOut: If the widget has input focus, transfer focus to the next or previous widget
+ in the focus chain, depending on \param direction.
+ \li RemoveInconsistent: Remove the widget, even if its focus chain is inconsistent.
+ \li AssertConsistency: qFatal, if the focus chain is inconsistent. This is used in the QWidget destructor.
+ \endlist
+ \return \c true if the widget has been removed, otherwise \c false.
+ */
+bool QWidgetPrivate::removeFromFocusChain(FocusChainRemovalRules rules, FocusDirection direction)
+{
+ Q_Q(QWidget);
+ if (!isFocusChainConsistent()) {
+#ifdef QT_DEBUG
+ if (rules.testFlag(FocusChainRemovalRule::AssertConsistency))
+ qFatal() << q << "has inconsistent focus chain.";
+#endif
+ qCDebug(lcWidgetFocus) << q << "wasn't removed, because of inconsistent focus chain.";
+ return false;
+ }
+
+ if (!isInFocusChain()) {
+ qCDebug(lcWidgetFocus) << q << "wasn't removed, because it is not part of a focus chain.";
+ return false;
+ }
+
+ if (rules.testFlag(FocusChainRemovalRule::EnsureFocusOut))
+ q->focusNextPrevChild(direction == FocusDirection::Next);
+
+ FOCUS_NEXT(FOCUS_PREV(q)) = FOCUS_NEXT(q);
+ FOCUS_PREV(FOCUS_NEXT(q)) = FOCUS_PREV(q);
+ initFocusChain();
+ qCDebug(lcWidgetFocus) << q << "removed from focus chain.";
+ return true;
+}
+
+/*!
+ \internal
+ Initialises the focus chain by making the widget point to itself.
+ */
+void QWidgetPrivate::initFocusChain()
+{
+ Q_Q(QWidget);
+ qCDebug(lcWidgetFocus) << "Initializing focus chain of" << q;
+ FOCUS_PREV(q) = q;
+ FOCUS_NEXT(q) = q;
+}
+
+/*!
+ \internal
+ Reads QWidget children, which are not part of a focus chain yet.
+ Inserts them into the focus chain before or after the widget,
+ depending on \param direction and in the order of their creation.
+ This is used, when QWidget::setParent() causes a widget to change toplevel windows.
+ */
+void QWidgetPrivate::reparentFocusChildren(FocusDirection direction)
+{
+ Q_Q(QWidget);
+ QWidgetList focusChildrenInsideChain;
+ QDuplicateTracker<QWidget *> seen;
+ QWidget *widget = q->nextInFocusChain();
+ while (q->isAncestorOf(widget)
+ && !seen.hasSeen(widget)
+ && widget != q->window()) {
+ if (widget->focusPolicy() != Qt::NoFocus)
+ focusChildrenInsideChain << widget;
+
+ widget = direction == FocusDirection::Next ? widget->nextInFocusChain()
+ : widget->previousInFocusChain();
+ }
+
+ const QWidgetList children = q->findChildren<QWidget *>(Qt::FindDirectChildrenOnly);
+ QWidgetList focusChildrenOutsideChain;
+ for (auto *child : children) {
+ if (!focusChildrenInsideChain.contains(child))
+ focusChildrenOutsideChain << child;
+ }
+ if (focusChildrenOutsideChain.isEmpty())
+ return;
+
+ QWidget *previous = q;
+ for (auto *child : focusChildrenOutsideChain) {
+ child->d_func()->insertIntoFocusChain(direction, previous);
+ previous = child;
+ }
+}
+
+/*!
+ \internal
+ Inserts a widget into the focus chain before or after \param position, depending on
+ \param direction.
+ \return \c true, if the insertion has changed the focus chain, otherwise \c false.
+ */
+bool QWidgetPrivate::insertIntoFocusChain(FocusDirection direction, QWidget *position)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(position);
+ QWidget *next = FOCUS_NEXT(q);
+ QWidget *previous = FOCUS_PREV(q);
+
+ switch (direction) {
+ case FocusDirection::Next:
+ if (previous == position) {
+ qCDebug(lcWidgetFocus) << "No-op insertion." << q << "is already before" << position;
+ return false;
+ }
+
+ removeFromFocusChain(FocusChainRemovalRule::AssertConsistency);
+
+ FOCUS_NEXT(q) = FOCUS_NEXT(position);
+ FOCUS_PREV(FOCUS_NEXT(position)) = q;
+ FOCUS_NEXT(position) = q;
+ FOCUS_PREV(q) = position;
+ qCDebug(lcWidgetFocus) << q << "inserted after" << position;
+ break;
+
+ case FocusDirection::Previous:
+ if (next == position) {
+ qCDebug(lcWidgetFocus) << "No-op insertion." << q << "is already after" << position;
+ return false;
+ }
+
+ removeFromFocusChain(FocusChainRemovalRule::AssertConsistency);
+
+ FOCUS_PREV(q) = FOCUS_PREV(position);
+ FOCUS_NEXT(FOCUS_PREV(position)) = q;
+ FOCUS_PREV(position) = q;
+ FOCUS_NEXT(q) = position;
+ qCDebug(lcWidgetFocus) << q << "inserted before" << position;
+ break;
+ }
+
+ Q_ASSERT(isFocusChainConsistent());
+ return true;
+}
+
+/*!
+ \internal
+ Convenience override to insert a QWidgetList \param toBeInserted into the focus chain
+ before or after \param position, depending on \param direction.
+ \return \c true, if the insertion has changed the focus chain, otherwise \c false.
+ \note
+ \param toBeInserted must be a consistent focus chain.
+ */
+bool QWidgetPrivate::insertIntoFocusChain(const QWidgetList &toBeInserted,
+ FocusDirection direction, QWidget *position)
+{
+ if (toBeInserted.isEmpty()) {
+ qCDebug(lcWidgetFocus) << "No-op insertion of an empty list";
+ return false;
+ }
+
+ Q_ASSERT_X(!toBeInserted.contains(position),
+ Q_FUNC_INFO,
+ "Coding error: toBeInserted contains position");
+
+ QWidget *first = toBeInserted.constFirst();
+ QWidget *last = toBeInserted.constLast();
+
+ // Call QWidget override to log accordingly
+ if (toBeInserted.count() == 1)
+ return first->d_func()->insertIntoFocusChain(direction, position);
+
+ Q_ASSERT(first != last);
+ switch (direction) {
+ case FocusDirection::Previous:
+ if (FOCUS_PREV(position) == last) {
+ qCDebug(lcWidgetFocus) << "No-op insertion." << toBeInserted << "is already before"
+ << position;
+ return false;
+ }
+ FOCUS_NEXT(FOCUS_PREV(position)) = first;
+ FOCUS_PREV(first) = FOCUS_PREV(position);
+ FOCUS_NEXT(last) = position;
+ FOCUS_PREV(position) = last;
+ qCDebug(lcWidgetFocus) << toBeInserted << "inserted before" << position;
+ break;
+ case FocusDirection::Next:
+ if (FOCUS_PREV(position) == last) {
+ qCDebug(lcWidgetFocus) << "No-op insertion." << toBeInserted << "is already after"
+ << position;
+ return false;
+ }
+ FOCUS_PREV(FOCUS_NEXT(position)) = last;
+ FOCUS_NEXT(last) = FOCUS_NEXT(position);
+ FOCUS_PREV(first) = position;
+ FOCUS_NEXT(position) = first;
+ qCDebug(lcWidgetFocus) << toBeInserted << "inserted after" << position;
+ break;
+ }
+
+ Q_ASSERT(position->d_func()->isFocusChainConsistent());
+ return true;
+}
+
+/*!
+ \internal
+ \return a QWidgetList, representing the part of the focus chain,
+ starting with \param from and ending with \param to, in \param direction.
+ */
+QWidgetList focusPath(QWidget *from, QWidget *to, QWidgetPrivate::FocusDirection direction)
+{
+ QWidgetList path({from});
+ if (from == to)
+ return path;
+
+ QWidget *current = from;
+ do {
+ switch (direction) {
+ case QWidgetPrivate::FocusDirection::Previous:
+ current = current->previousInFocusChain();
+ break;
+ case QWidgetPrivate::FocusDirection::Next:
+ current = current->nextInFocusChain();
+ break;
+ }
+ if (path.contains(current))
+ return QWidgetList();
+ path << current;
+ } while (current != to);
+
+ return path;
+}
+
+/*!
+ \internal
+ Removes the part from the focus chain starting with \param from and ending with \param to,
+ in \param direction.
+ \return removed part as a QWidgetList.
+ */
+QWidgetList QWidgetPrivate::takeFromFocusChain(QWidget *from,
+ QWidget *to,
+ FocusDirection direction)
+{
+ // Check if there is a path from->to in direction
+ const QWidgetList path = focusPath(from, to , direction);
+ if (path.isEmpty()) {
+ qCDebug(lcWidgetFocus) << "No-op removal. Focus chain from" << from << "doesn't lead to " << to;
+ return QWidgetList();
+ }
+
+ QWidget *first = path.constFirst();
+ QWidget *last = path.constLast();
+ if (first == last) {
+ first->d_func()->removeFromFocusChain();
+ return QWidgetList({first});
+ }
+
+ FOCUS_NEXT(FOCUS_PREV(first)) = FOCUS_NEXT(last);
+ FOCUS_PREV(FOCUS_NEXT(last)) = FOCUS_PREV(first);
+ FOCUS_PREV(first) = last;
+ FOCUS_NEXT(last) = first;
+ qCDebug(lcWidgetFocus) << path << "removed from focus chain";
+ return path;
+}
+
+/*!
+ \internal
+ \return The last focus child of the widget, traversing the focus chain no further than
+ \param noFurtherThan.
+ */
+QWidget *QWidgetPrivate::determineLastFocusChild(QWidget *noFurtherThan)
+{
+ Q_Q(QWidget);
+ // 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.
+ QWidget *lastFocusChild = q;
+
+ QWidget *focusProxy = deepestFocusProxy();
+ if (!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 : std::as_const(q->children())) {
+ QWidget *w = qobject_cast<QWidget *>(object);
+ if (w && w->focusProxy() == q) {
+ lastFocusChild = w;
+ break;
+ }
+ }
+ } else if (q->isAncestorOf(focusProxy)) {
+ lastFocusChild = focusProxy;
+ for (QWidget *focusNext = lastFocusChild->nextInFocusChain();
+ focusNext != focusProxy && q->isAncestorOf(focusNext)
+ && focusNext->window() == focusProxy->window();
+ focusNext = focusNext->nextInFocusChain()) {
+ if (focusNext == noFurtherThan)
+ break;
+ if (focusNext->focusPolicy() != Qt::NoFocus)
+ lastFocusChild = focusNext;
+ }
+ }
+ return lastFocusChild;
+};
+
+/*!
+ \internal
+ \return \c true, if the widget is part of a focus chain and \c false otherwise.
+ A widget is considered to be part of a focus chain, neither FOCUS_NEXT, nor FOCUS_PREV
+ are pointing to the widget itself.
+
+ \note
+ This method doesn't check the consistency of the focus chain.
+ If multiple widgets have been removed from the focus chain by takeFromFocusChain(),
+ isInFocusChain() will return \c true for all of those widgets, even if they represent
+ an inconsistent focus chain.
+ */
+bool QWidgetPrivate::isInFocusChain() const
+{
+ Q_Q(const QWidget);
+ return !(FOCUS_NEXT(q) == q && FOCUS_PREV(q) == q);
+}
+
+/*!
+ \internal
+ A focus chain is consistent, when it is circular: Following the chain in either direction
+ has to return to the beginning. This is why a newly constructed widget points to itself,
+ when the focus chain has been initialized. A newly constructed widget is considered to have
+ a consistent focus chain, while not being part of a focus chain.
+
+ The method always returns \c true, when the logging category "qt.widgets.focus" is disabled.
+ When it is enabled, the method returns \c true early, if a widget is pointing to itself.
+ It returns \c false, if one of the following is detected:
+ \list
+ \li nullptr found in a previous/next pointer.
+ \li broken chain: widget A is B's previous, but B isn't A's next.
+ \li chain isn't closed: starting at A doesn't lead back to A.
+ \endlist
+ It return \c true, if none of the above is observed.
+
+ \note
+ The focus chain is checked only in forward direction.
+ This is sufficient, because the check for a broken chain asserts consistent paths
+ in both directions.
+ */
+bool QWidgetPrivate::isFocusChainConsistent() const
+{
+ Q_Q(const QWidget);
+ const bool skip = !QLoggingCategory("qt.widgets.focus").isDebugEnabled();
+ if (skip)
+ return true;
+
+ if (!isInFocusChain())
+ return true;
+
+ const QWidget *position = q;
+
+ for (int i = 0; i < QApplication::allWidgets().count(); ++i) {
+ if (!FOCUS_PREV(position) || !FOCUS_NEXT(position)) {
+ qCDebug(lcWidgetFocus) << "Nullptr found at:" << position
+ << "Previous pointing to" << FOCUS_PREV(position)
+ << "Next pointing to" << FOCUS_NEXT(position);
+ return false;
+ }
+ if (!(FOCUS_PREV(FOCUS_NEXT(position)) == position
+ && FOCUS_NEXT(FOCUS_PREV(position)) == position)) {
+ qCDebug(lcWidgetFocus) << "Inconsistent focus chain at:" << position
+ << "Previous pointing to" << FOCUS_PREV(FOCUS_NEXT(position))
+ << "Next pointing to" << FOCUS_NEXT(FOCUS_PREV(position));
+ return false;
+ }
+ position = FOCUS_NEXT(position);
+ if (position == q)
+ return true;
+
+ }
+
+ qCDebug(lcWidgetFocus) << "Focus chain leading from" << q << "to" << position << "is not closed.";
+ return false;
+}
+
+#undef FOCUS_NEXT
+#undef FOCUS_PREV
+
+
QT_END_NAMESPACE
#include "moc_qwidget.cpp"
diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h
index d16d0f438c..9fab9efa62 100644
--- a/src/widgets/kernel/qwidget_p.h
+++ b/src/widgets/kernel/qwidget_p.h
@@ -41,6 +41,7 @@
#include <private/qgesture_p.h>
#include <qpa/qplatformbackingstore.h>
#include <QtGui/private/qbackingstorerhisupport_p.h>
+#include <private/qapplication_p.h>
#include <QtCore/qpointer.h>
@@ -733,6 +734,40 @@ public:
uint childrenHiddenByWState : 1;
uint childrenShownByExpose : 1;
+ // *************************** Focus abstraction ************************************
+ enum class FocusDirection {
+ Previous,
+ Next,
+ };
+
+ enum class FocusChainRemovalRule {
+ EnsureFocusOut = 0x01,
+ AssertConsistency = 0x02,
+ };
+ Q_DECLARE_FLAGS(FocusChainRemovalRules, FocusChainRemovalRule)
+
+ // Getters
+ QWidget *nextPrevElementInFocusChain(FocusDirection direction) const;
+
+ // manipulators
+ bool removeFromFocusChain(FocusChainRemovalRules rules = FocusChainRemovalRules(),
+ FocusDirection direction = FocusDirection::Next);
+ bool insertIntoFocusChain(FocusDirection direction, QWidget *position);
+ static bool insertIntoFocusChain(const QWidgetList &toBeInserted, FocusDirection direction, QWidget *position);
+ bool insertIntoFocusChainBefore(QWidget *position)
+ { return insertIntoFocusChain(FocusDirection::Previous, position); }
+ bool insertIntoFocusChainAfter(QWidget *position)
+ { return insertIntoFocusChain(FocusDirection::Next, position); }
+ static QWidgetList takeFromFocusChain(QWidget *from, QWidget *to,
+ FocusDirection direction = FocusDirection::Next);
+ void reparentFocusChildren(FocusDirection direction);
+ QWidget *determineLastFocusChild(QWidget *noFurtherThan);
+
+ // Initialization and tests
+ void initFocusChain();
+ bool isInFocusChain() const;
+ bool isFocusChainConsistent() const;
+
// *************************** Platform specific ************************************
#if defined(Q_OS_WIN)
uint noPaintOnScreen : 1; // see qwidget.cpp ::paintEngine()
diff --git a/src/widgets/kernel/qwidgetrepaintmanager.cpp b/src/widgets/kernel/qwidgetrepaintmanager.cpp
index abe5e8016e..607a767a20 100644
--- a/src/widgets/kernel/qwidgetrepaintmanager.cpp
+++ b/src/widgets/kernel/qwidgetrepaintmanager.cpp
@@ -355,7 +355,11 @@ void QWidgetRepaintManager::sendUpdateRequest(QWidget *widget, UpdateTime update
switch (updateTime) {
case UpdateLater:
- updateRequestSent = true;
+ // Prevent redundant update request events, unless it's a
+ // paint on screen widget, as these don't go through the
+ // normal backingstore sync machinery.
+ if (!widget->d_func()->shouldPaintOnScreen())
+ updateRequestSent = true;
QCoreApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
break;
case UpdateNow: {
diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp
index f51baba88b..e7f0a84004 100644
--- a/src/widgets/kernel/qwidgetwindow.cpp
+++ b/src/widgets/kernel/qwidgetwindow.cpp
@@ -77,6 +77,39 @@ public:
widget->focusWidget()->clearFocus();
}
+ void setFocusToTarget(FocusTarget target, Qt::FocusReason reason) override
+ {
+ Q_Q(QWidgetWindow);
+ QWidget *widget = q->widget();
+ if (!widget)
+ return;
+ QWidget *newFocusWidget = nullptr;
+
+ switch (target) {
+ case FocusTarget::First:
+ newFocusWidget = q->getFocusWidget(QWidgetWindow::FirstFocusWidget);
+ break;
+ case FocusTarget::Last:
+ newFocusWidget = q->getFocusWidget(QWidgetWindow::LastFocusWidget);
+ break;
+ case FocusTarget::Next: {
+ QWidget *focusWidget = widget->focusWidget() ? widget->focusWidget() : widget;
+ newFocusWidget = focusWidget->nextInFocusChain() ? focusWidget->nextInFocusChain() : focusWidget;
+ break;
+ }
+ case FocusTarget::Prev: {
+ QWidget *focusWidget = widget->focusWidget() ? widget->focusWidget() : widget;
+ newFocusWidget = focusWidget->previousInFocusChain() ? focusWidget->previousInFocusChain() : focusWidget;
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (newFocusWidget)
+ newFocusWidget->setFocus(reason);
+ }
+
QRectF closestAcceptableGeometry(const QRectF &rect) const override;
void processSafeAreaMarginsChanged() override
diff --git a/src/widgets/kernel/qwindowcontainer.cpp b/src/widgets/kernel/qwindowcontainer.cpp
index cfd785a82c..1aaf04af43 100644
--- a/src/widgets/kernel/qwindowcontainer.cpp
+++ b/src/widgets/kernel/qwindowcontainer.cpp
@@ -5,6 +5,7 @@
#include "qwidget_p.h"
#include "qwidgetwindow_p.h"
#include <QtGui/qwindow.h>
+#include <QtGui/private/qwindow_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
#include <QDebug>
@@ -28,7 +29,6 @@ public:
QWindowContainerPrivate()
: window(nullptr)
- , oldFocusWindow(nullptr)
, usesNativeWidgets(false)
{
}
@@ -103,7 +103,6 @@ public:
}
QPointer<QWindow> window;
- QWindow *oldFocusWindow;
QWindow fakeParent;
uint usesNativeWidgets : 1;
@@ -207,6 +206,7 @@ QWindowContainer::QWindowContainer(QWindow *embeddedWindow, QWidget *parent, Qt:
}
d->window = embeddedWindow;
+ d->window->installEventFilter(this);
QString windowName = d->window->objectName();
if (windowName.isEmpty())
@@ -219,8 +219,8 @@ QWindowContainer::QWindowContainer(QWindow *embeddedWindow, QWidget *parent, Qt:
setAcceptDrops(true);
- connect(qGuiApp, &QGuiApplication::focusWindowChanged,
- this, &QWindowContainer::focusWindowChanged);
+ connect(containedWindow(), &QWindow::minimumHeightChanged, this, &QWindowContainer::updateGeometry);
+ connect(containedWindow(), &QWindow::minimumWidthChanged, this, &QWindowContainer::updateGeometry);
}
QWindow *QWindowContainer::containedWindow() const
@@ -241,30 +241,12 @@ QWindowContainer::~QWindowContainer()
// QEvent::PlatformSurface delivery relies on virtuals. Getting
// SurfaceAboutToBeDestroyed can be essential for OpenGL, Vulkan, etc.
// QWindow subclasses in particular. Keep these working.
- if (d->window)
+ if (d->window) {
+ d->window->removeEventFilter(this);
d->window->destroy();
+ }
delete d->window;
-
- disconnect(qGuiApp, &QGuiApplication::focusWindowChanged,
- this, &QWindowContainer::focusWindowChanged);
-}
-
-
-
-/*!
- \internal
- */
-
-void QWindowContainer::focusWindowChanged(QWindow *focusWindow)
-{
- Q_D(QWindowContainer);
- d->oldFocusWindow = focusWindow;
- if (focusWindow == d->window) {
- QWidget *widget = QApplication::focusWidget();
- if (widget)
- widget->clearFocus();
- }
}
/*!
@@ -281,8 +263,12 @@ bool QWindowContainer::eventFilter(QObject *o, QEvent *e)
QChildEvent *ce = static_cast<QChildEvent *>(e);
if (ce->child() == d->window) {
o->removeEventFilter(this);
+ d->window->removeEventFilter(this);
d->window = nullptr;
}
+ } else if (e->type() == QEvent::FocusIn) {
+ if (o == d->window)
+ setFocus(Qt::ActiveWindowFocusReason);
}
return false;
}
@@ -332,11 +318,16 @@ bool QWindowContainer::event(QEvent *e)
break;
case QEvent::FocusIn:
if (d->window->parent()) {
- if (d->oldFocusWindow != d->window) {
+ if (QGuiApplication::focusWindow() != d->window) {
+ QFocusEvent *event = static_cast<QFocusEvent *>(e);
+ const auto reason = event->reason();
+ QWindowPrivate::FocusTarget target = QWindowPrivate::FocusTarget::Current;
+ if (reason == Qt::TabFocusReason)
+ target = QWindowPrivate::FocusTarget::First;
+ else if (reason == Qt::BacktabFocusReason)
+ target = QWindowPrivate::FocusTarget::Last;
+ qt_window_private(d->window)->setFocusToTarget(target, reason);
d->window->requestActivate();
- } else {
- QWidget *next = nextInFocusChain();
- next->setFocus();
}
}
break;
@@ -373,6 +364,11 @@ bool QWindowContainer::event(QEvent *e)
return QWidget::event(e);
}
+QSize QWindowContainer::minimumSizeHint() const
+{
+ return containedWindow() ? containedWindow()->minimumSize() : QSize(0, 0);
+}
+
typedef void (*qwindowcontainer_traverse_callback)(QWidget *parent);
static void qwindowcontainer_traverse(QWidget *parent, qwindowcontainer_traverse_callback callback)
{
diff --git a/src/widgets/kernel/qwindowcontainer_p.h b/src/widgets/kernel/qwindowcontainer_p.h
index 8dc5c64af4..0cbcc5321d 100644
--- a/src/widgets/kernel/qwindowcontainer_p.h
+++ b/src/widgets/kernel/qwindowcontainer_p.h
@@ -31,6 +31,7 @@ public:
explicit QWindowContainer(QWindow *embeddedWindow, QWidget *parent = nullptr, Qt::WindowFlags f = { });
~QWindowContainer();
QWindow *containedWindow() const;
+ QSize minimumSizeHint() const override;
static void toplevelAboutToBeDestroyed(QWidget *parent);
static void parentWasChanged(QWidget *parent);
@@ -41,9 +42,6 @@ public:
protected:
bool event(QEvent *ev) override;
bool eventFilter(QObject *, QEvent *ev) override;
-
-private slots:
- void focusWindowChanged(QWindow *focusWindow);
};
QT_END_NAMESPACE