summaryrefslogtreecommitdiffstats
path: root/src/widgets/kernel/qwidget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets/kernel/qwidget.cpp')
-rw-r--r--src/widgets/kernel/qwidget.cpp245
1 files changed, 168 insertions, 77 deletions
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp
index 1be158af63..0af28e3cfd 100644
--- a/src/widgets/kernel/qwidget.cpp
+++ b/src/widgets/kernel/qwidget.cpp
@@ -1029,10 +1029,13 @@ void QWidgetPrivate::createRecursively()
QRhi *QWidgetPrivate::rhi() const
{
- if (QWidgetRepaintManager *repaintManager = maybeRepaintManager())
- return repaintManager->rhi();
- else
+ Q_Q(const QWidget);
+ if (auto *backingStore = q->backingStore()) {
+ auto *window = windowHandle(WindowHandleMode::Closest);
+ return backingStore->handle()->rhi(window);
+ } else {
return nullptr;
+ }
}
/*!
@@ -1112,8 +1115,15 @@ static bool q_evaluateRhiConfigRecursive(const QWidget *w, QPlatformBackingStore
}
for (const QObject *child : w->children()) {
if (const QWidget *childWidget = qobject_cast<const QWidget *>(child)) {
- if (q_evaluateRhiConfigRecursive(childWidget, outConfig, outType))
+ if (q_evaluateRhiConfigRecursive(childWidget, outConfig, outType)) {
+ static bool optOut = qEnvironmentVariableIsSet("QT_WIDGETS_NO_CHILD_RHI");
+ // Native child widgets should not trigger RHI for its parent
+ // hierarchy, but will still flush the native child using RHI.
+ if (!optOut && childWidget->testAttribute(Qt::WA_NativeWindow))
+ continue;
+
return true;
+ }
}
}
return false;
@@ -1289,7 +1299,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
@@ -1356,19 +1366,19 @@ void QWidgetPrivate::create()
QBackingStore *store = q->backingStore();
usesRhiFlush = false;
- if (!store) {
- if (q->windowType() != Qt::Desktop) {
- if (q->isWindow()) {
- q->setBackingStore(new QBackingStore(win));
- QPlatformBackingStoreRhiConfig rhiConfig;
- usesRhiFlush = q_evaluateRhiConfig(q, &rhiConfig, nullptr);
- topData()->backingStore->handle()->setRhiConfig(rhiConfig);
- }
- } else {
- q->setAttribute(Qt::WA_PaintOnScreen, true);
+ if (q->windowType() == Qt::Desktop) {
+ q->setAttribute(Qt::WA_PaintOnScreen, true);
+ } else {
+ if (!store && q->isWindow())
+ q->setBackingStore(new QBackingStore(win));
+
+ QPlatformBackingStoreRhiConfig rhiConfig;
+ usesRhiFlush = q_evaluateRhiConfig(q, &rhiConfig, nullptr);
+ if (usesRhiFlush && q->backingStore()) {
+ // Trigger creation of support infrastructure up front,
+ // now that we have a specific RHI configuration.
+ q->backingStore()->handle()->createRhi(win, rhiConfig);
}
- } else if (win->handle()) {
- usesRhiFlush = q_evaluateRhiConfig(q, nullptr, nullptr);
}
setWindowModified_helper();
@@ -2715,8 +2725,10 @@ void QWidgetPrivate::inheritStyle()
// to be running a proxy
if (!qApp->styleSheet().isEmpty() || qt_styleSheet(parentStyle)) {
QStyle *newStyle = parentStyle;
- if (q->testAttribute(Qt::WA_SetStyle))
+ if (q->testAttribute(Qt::WA_SetStyle) && qt_styleSheet(origStyle) == nullptr)
newStyle = new QStyleSheetStyle(origStyle);
+ else if (auto *styleSheetStyle = qt_styleSheet(origStyle))
+ newStyle = styleSheetStyle;
else if (QStyleSheetStyle *newProxy = qt_styleSheet(parentStyle))
newProxy->ref();
@@ -5160,6 +5172,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()) {
@@ -5168,6 +5181,7 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset,
} else {
enginePriv->setSystemViewport(oldSystemClip);
}
+ painter->setLayoutDirection(layoutDirection());
d->render(target, targetOffset, toBePainted, renderFlags);
@@ -5175,6 +5189,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);
@@ -6104,6 +6119,13 @@ void QWidget::setWindowTitle(const QString &title)
if (QWidget::windowTitle() == title && !title.isEmpty() && !title.isNull())
return;
+#if QT_CONFIG(accessibility)
+ QString oldAccessibleName;
+ const QAccessibleInterface *accessible = QAccessible::queryAccessibleInterface(this);
+ if (accessible)
+ oldAccessibleName = accessible->text(QAccessible::Name);
+#endif
+
Q_D(QWidget);
d->topData()->caption = title;
d->setWindowTitle_helper(title);
@@ -6112,6 +6134,13 @@ void QWidget::setWindowTitle(const QString &title)
QCoreApplication::sendEvent(this, &e);
emit windowTitleChanged(title);
+
+#if QT_CONFIG(accessibility)
+ if (accessible && accessible->text(QAccessible::Name) != oldAccessibleName) {
+ QAccessibleEvent event(this, QAccessible::NameChanged);
+ QAccessible::updateAccessibility(&event);
+ }
+#endif
}
@@ -6616,7 +6645,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) {
@@ -7074,7 +7105,6 @@ void QWidgetPrivate::reparentFocusWidgets(QWidget * oldtlw)
if (focus_child)
focus_child->clearFocus();
- insertIntoFocusChain(QWidgetPrivate::FocusDirection::Previous, q->window());
reparentFocusChildren(QWidgetPrivate::FocusDirection::Next);
}
@@ -7689,11 +7719,15 @@ QMargins QWidgetPrivate::safeAreaMargins() const
return QMargins();
// Or, if one of our ancestors are in a layout that does not have WA_LayoutOnEntireRect
- // set, then we know that the layout has already taken care of placing us inside the
- // safe area, by taking the contents rect of its parent widget into account.
+ // set, and the widget respects the safe area, then we know that the layout has already
+ // taken care of placing us inside the safe area, by taking the contents rect of its
+ // parent widget into account.
const QWidget *assumedSafeWidget = nullptr;
for (const QWidget *w = q; w != nativeWidget; w = w->parentWidget()) {
QWidget *parentWidget = w->parentWidget();
+ if (!parentWidget->testAttribute(Qt::WA_ContentsMarginsRespectsSafeArea))
+ continue; // Layout can't help us
+
if (parentWidget->testAttribute(Qt::WA_LayoutOnEntireRect))
continue; // Layout not going to help us
@@ -10650,6 +10684,8 @@ 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);
+ QWidget *oldParentWithWindow = d->closestParentWidgetWithWindowHandle();
if (f & Qt::Window) // Frame geometry likely changes, refresh.
d->data.fstrut_dirty = true;
@@ -10692,7 +10728,8 @@ 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 ((oldtlw && oldtlw->d_func()->usesRhiFlush) && ((!parent && parentWidget()) || (parent && parent->window() != oldtlw)))
+ const bool oldParentUsesRhiFlush = oldParentWithWindow ? oldParentWithWindow->d_func()->usesRhiFlush : false;
+ if (oldParentUsesRhiFlush && ((!parent && parentWidget()) || (parent && parent->window() != oldtlw)))
qSendWindowChangeToTextureChildrenRecursively(this, QEvent::WindowAboutToChangeInternal);
// If we get parented into another window, children will be folded
@@ -10773,7 +10810,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 ((oldtlw && oldtlw->d_func()->usesRhiFlush) && oldtlw != window())
+ if (oldParentUsesRhiFlush && oldtlw != window())
qSendWindowChangeToTextureChildrenRecursively(this, QEvent::WindowChangeInternal);
if (!wasCreated) {
@@ -10801,33 +10838,47 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
if (d->extra && d->extra->hasWindowContainer)
QWindowContainer::parentWasChanged(this);
- QWidget *newtlw = window();
- if (oldtlw != newtlw) {
+ QWidget *newParentWithWindow = d->closestParentWidgetWithWindowHandle();
+ if (newParentWithWindow && newParentWithWindow != oldParentWithWindow) {
+ // Check if the native parent now needs to switch to RHI
+ qCDebug(lcWidgetPainting) << "Evaluating whether reparenting of" << this
+ << "into" << parent << "requires RHI enablement for" << newParentWithWindow;
+
+ QPlatformBackingStoreRhiConfig rhiConfig;
QSurface::SurfaceType surfaceType = QSurface::RasterSurface;
- // Only evaluate the reparented subtree. While it might be tempting to
- // do it on newtlw instead, the performance implications of that are
+
+ // First evaluate whether the reparented widget uses RHI.
+ // We do this as a separate step because the performance
+ // implications of always checking the native parent are
// problematic when it comes to large widget trees.
- if (q_evaluateRhiConfig(this, nullptr, &surfaceType)) {
- const bool wasUsingRhiFlush = newtlw->d_func()->usesRhiFlush;
- newtlw->d_func()->usesRhiFlush = true;
- bool recreate = false;
- if (QWindow *w = newtlw->windowHandle()) {
- if (w->surfaceType() != surfaceType || !wasUsingRhiFlush)
- recreate = true;
- }
- // QTBUG-115652: Besides the toplevel the nativeParentWidget()'s QWindow must be checked as well.
- if (QWindow *w = d->windowHandle(QWidgetPrivate::WindowHandleMode::Closest)) {
- if (w->surfaceType() != surfaceType)
- recreate = true;
- }
- if (recreate) {
- const auto windowStateBeforeDestroy = newtlw->windowState();
- const auto visibilityBeforeDestroy = newtlw->isVisible();
- newtlw->destroy();
- newtlw->create();
- Q_ASSERT(newtlw->windowHandle());
- newtlw->windowHandle()->setWindowStates(windowStateBeforeDestroy);
- QWidgetPrivate::get(newtlw)->setVisible(visibilityBeforeDestroy);
+ if (q_evaluateRhiConfig(this, &rhiConfig, &surfaceType)) {
+ // Then check whether the native parent requires RHI
+ // as a result. It may not, if this widget is a native
+ // window, and can handle its own RHI flushing.
+ if (q_evaluateRhiConfig(newParentWithWindow, nullptr, nullptr)) {
+ // Finally, check whether we need to recreate the
+ // native parent to enable RHI flushing.
+ auto *existingWindow = newParentWithWindow->windowHandle();
+ auto existingSurfaceType = existingWindow->surfaceType();
+ if (existingSurfaceType != surfaceType) {
+ qCDebug(lcWidgetPainting)
+ << "Recreating" << existingWindow
+ << "with current type" << existingSurfaceType
+ << "to support" << surfaceType;
+ const auto windowStateBeforeDestroy = newParentWithWindow->windowState();
+ const auto visibilityBeforeDestroy = newParentWithWindow->isVisible();
+ newParentWithWindow->destroy();
+ newParentWithWindow->create();
+ Q_ASSERT(newParentWithWindow->windowHandle());
+ newParentWithWindow->windowHandle()->setWindowStates(windowStateBeforeDestroy);
+ QWidgetPrivate::get(newParentWithWindow)->setVisible(visibilityBeforeDestroy);
+ } else if (auto *backingStore = newParentWithWindow->backingStore()) {
+ // If we don't recreate we still need to make sure the native parent
+ // widget has a RHI config that the reparented widget can use.
+ backingStore->handle()->createRhi(existingWindow, rhiConfig);
+ // And that it knows it's now flushing with RHI
+ QWidgetPrivate::get(newParentWithWindow)->usesRhiFlush = true;
+ }
}
}
}
@@ -12285,8 +12336,10 @@ QBackingStore *QWidget::backingStore() const
if (extra && extra->backingStore)
return extra->backingStore;
- QWidgetRepaintManager *repaintManager = d->maybeRepaintManager();
- return repaintManager ? repaintManager->backingStore() : nullptr;
+ if (!isWindow())
+ return window()->backingStore();
+
+ return nullptr;
}
void QWidgetPrivate::getLayoutItemMargins(int *left, int *top, int *right, int *bottom) const
@@ -12933,6 +12986,10 @@ int QWidget::metric(PaintDeviceMetric m) const
return resolveDevicePixelRatio();
case PdmDevicePixelRatioScaled:
return QPaintDevice::devicePixelRatioFScale() * resolveDevicePixelRatio();
+ case PdmDevicePixelRatioF_EncodedA:
+ Q_FALLTHROUGH();
+ case PdmDevicePixelRatioF_EncodedB:
+ return QPaintDevice::encodeMetricF(m, resolveDevicePixelRatio());
default:
break;
}
@@ -13345,32 +13402,61 @@ void QWidgetPrivate::initFocusChain()
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;
+ // separate the focus chain into new (children of myself) and old (the rest)
+ QWidget *firstOld = nullptr;
+ QWidget *lastOld = nullptr; // last in the old list
+ QWidget *lastNew = q; // last in the new list
+ bool prevWasNew = true;
+ QWidget *widget = nextPrevElementInFocusChain(direction);
+
+ // For efficiency, do not maintain the list invariant inside the loop.
+ // Append items to the relevant list, and we optimize by not changing pointers,
+ // when subsequent items are going into the same list.
+ while (widget != q) {
+ bool currentIsNew = q->isAncestorOf(widget);
+ if (currentIsNew) {
+ if (!prevWasNew) {
+ // previous was old => append to new list
+ FOCUS_NEXT(lastNew) = widget;
+ FOCUS_PREV(widget) = lastNew;
+ }
+ lastNew = widget;
+ } else {
+ if (prevWasNew) {
+ // prev was new => append to old list, if it exists
+ if (lastOld) {
+ FOCUS_NEXT(lastOld) = widget;
+ FOCUS_PREV(widget) = lastOld;
+ } else {
+ // start the old list
+ firstOld = widget;
+ }
+ }
+ lastOld = widget;
+ }
+ widget = widget->d_func()->nextPrevElementInFocusChain(direction);
+ prevWasNew = currentIsNew;
+ }
+
+ // repair old list:
+ if (firstOld) {
+ FOCUS_NEXT(lastOld) = firstOld;
+ FOCUS_PREV(firstOld) = lastOld;
+ }
+
+ if (!q->isWindow()) {
+ QWidget *topLevel = q->window();
+ // insert new chain into toplevel's chain
+ QWidget *prev = FOCUS_PREV(topLevel);
+ FOCUS_PREV(topLevel) = lastNew;
+ FOCUS_NEXT(prev) = q;
+ FOCUS_PREV(q) = prev;
+ FOCUS_NEXT(lastNew) = topLevel;
+ } else {
+ // repair new list
+ FOCUS_NEXT(lastNew) = q;
+ FOCUS_PREV(q) = lastNew;
}
}
@@ -13611,7 +13697,8 @@ bool QWidgetPrivate::isInFocusChain() const
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.
- This method returns \c true early, if a widget is pointing to itself.
+ 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.
@@ -13628,6 +13715,10 @@ bool QWidgetPrivate::isInFocusChain() const
bool QWidgetPrivate::isFocusChainConsistent() const
{
Q_Q(const QWidget);
+ const bool skip = !QLoggingCategory("qt.widgets.focus").isDebugEnabled();
+ if (skip)
+ return true;
+
if (!isInFocusChain())
return true;