diff options
Diffstat (limited to 'src/widgets/kernel/qwidget.cpp')
-rw-r--r-- | src/widgets/kernel/qwidget.cpp | 245 |
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; |