| 1 | // Copyright (C) 2020 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | |
| 4 | #include "qquickwindow.h" |
| 5 | #include "qquickwindow_p.h" |
| 6 | |
| 7 | #include "qquickitem.h" |
| 8 | #include "qquickitem_p.h" |
| 9 | #include "qquickevents_p_p.h" |
| 10 | #include "qquickgraphicsdevice_p.h" |
| 11 | #include "qquickwindowcontainer_p.h" |
| 12 | |
| 13 | #include <QtQuick/private/qsgrenderer_p.h> |
| 14 | #include <QtQuick/private/qsgplaintexture_p.h> |
| 15 | #include <QtQuick/private/qquickpointerhandler_p.h> |
| 16 | #include <QtQuick/private/qquickpointerhandler_p_p.h> |
| 17 | #include <private/qsgrenderloop_p.h> |
| 18 | #include <private/qsgrhisupport_p.h> |
| 19 | #include <private/qquickrendercontrol_p.h> |
| 20 | #include <private/qquickanimatorcontroller_p.h> |
| 21 | #include <private/qquickprofiler_p.h> |
| 22 | #include <private/qquicktextinterface_p.h> |
| 23 | |
| 24 | #include <private/qguiapplication_p.h> |
| 25 | |
| 26 | #include <private/qabstractanimation_p.h> |
| 27 | |
| 28 | #include <QtGui/qpainter.h> |
| 29 | #include <QtGui/qevent.h> |
| 30 | #include <QtGui/qmatrix4x4.h> |
| 31 | #include <QtGui/private/qevent_p.h> |
| 32 | #include <QtGui/private/qpointingdevice_p.h> |
| 33 | #include <QtCore/qvarlengtharray.h> |
| 34 | #include <QtCore/qabstractanimation.h> |
| 35 | #include <QtCore/QLibraryInfo> |
| 36 | #include <QtCore/QRunnable> |
| 37 | #include <QtQml/qqmlincubator.h> |
| 38 | #include <QtQml/qqmlinfo.h> |
| 39 | #include <QtQml/private/qqmlmetatype_p.h> |
| 40 | |
| 41 | #include <QtQuick/private/qquickpixmap_p.h> |
| 42 | |
| 43 | #include <private/qqmldebugserviceinterfaces_p.h> |
| 44 | #include <private/qqmldebugconnector_p.h> |
| 45 | #include <private/qsgdefaultrendercontext_p.h> |
| 46 | #include <private/qsgsoftwarerenderer_p.h> |
| 47 | #if QT_CONFIG(opengl) |
| 48 | #include <private/qopengl_p.h> |
| 49 | #include <QOpenGLContext> |
| 50 | #endif |
| 51 | #ifndef QT_NO_DEBUG_STREAM |
| 52 | #include <private/qdebug_p.h> |
| 53 | #endif |
| 54 | #include <QtCore/qpointer.h> |
| 55 | |
| 56 | #include <rhi/qrhi.h> |
| 57 | |
| 58 | #include <utility> |
| 59 | #include <mutex> |
| 60 | |
| 61 | QT_BEGIN_NAMESPACE |
| 62 | |
| 63 | Q_DECLARE_LOGGING_CATEGORY(lcHoverTrace) |
| 64 | Q_DECLARE_LOGGING_CATEGORY(lcMouse) |
| 65 | Q_DECLARE_LOGGING_CATEGORY(lcTouch) |
| 66 | Q_DECLARE_LOGGING_CATEGORY(lcPtr) |
| 67 | Q_LOGGING_CATEGORY(lcDirty, "qt.quick.dirty" ) |
| 68 | Q_LOGGING_CATEGORY(lcQuickWindow, "qt.quick.window" ) |
| 69 | Q_LOGGING_CATEGORY(lcTransient, "qt.quick.window.transient" ) |
| 70 | |
| 71 | bool QQuickWindowPrivate::defaultAlphaBuffer = false; |
| 72 | |
| 73 | #if defined(QT_QUICK_DEFAULT_TEXT_RENDER_TYPE) |
| 74 | QQuickWindow::TextRenderType QQuickWindowPrivate::textRenderType = QQuickWindow::QT_QUICK_DEFAULT_TEXT_RENDER_TYPE; |
| 75 | #else |
| 76 | QQuickWindow::TextRenderType QQuickWindowPrivate::textRenderType = QQuickWindow::QtTextRendering; |
| 77 | #endif |
| 78 | |
| 79 | class QQuickWindowIncubationController : public QObject, public QQmlIncubationController |
| 80 | { |
| 81 | Q_OBJECT |
| 82 | |
| 83 | public: |
| 84 | QQuickWindowIncubationController(QSGRenderLoop *loop) |
| 85 | : m_renderLoop(loop), m_timer(0) |
| 86 | { |
| 87 | // Allow incubation for 1/3 of a frame. |
| 88 | m_incubation_time = qMax(a: 1, b: int(1000 / QGuiApplication::primaryScreen()->refreshRate()) / 3); |
| 89 | |
| 90 | QAnimationDriver *animationDriver = m_renderLoop->animationDriver(); |
| 91 | if (animationDriver) { |
| 92 | connect(sender: animationDriver, signal: &QAnimationDriver::stopped, context: this, slot: &QQuickWindowIncubationController::animationStopped); |
| 93 | connect(sender: m_renderLoop, signal: &QSGRenderLoop::timeToIncubate, context: this, slot: &QQuickWindowIncubationController::incubate); |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | protected: |
| 98 | void timerEvent(QTimerEvent *) override |
| 99 | { |
| 100 | killTimer(id: m_timer); |
| 101 | m_timer = 0; |
| 102 | incubate(); |
| 103 | } |
| 104 | |
| 105 | void incubateAgain() { |
| 106 | if (m_timer == 0) { |
| 107 | // Wait for a while before processing the next batch. Using a |
| 108 | // timer to avoid starvation of system events. |
| 109 | m_timer = startTimer(interval: m_incubation_time); |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | public slots: |
| 114 | void incubate() { |
| 115 | if (m_renderLoop && incubatingObjectCount()) { |
| 116 | if (m_renderLoop->interleaveIncubation()) { |
| 117 | incubateFor(msecs: m_incubation_time); |
| 118 | } else { |
| 119 | incubateFor(msecs: m_incubation_time * 2); |
| 120 | if (incubatingObjectCount()) |
| 121 | incubateAgain(); |
| 122 | } |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | void animationStopped() { incubate(); } |
| 127 | |
| 128 | protected: |
| 129 | void incubatingObjectCountChanged(int count) override |
| 130 | { |
| 131 | if (count && m_renderLoop && !m_renderLoop->interleaveIncubation()) |
| 132 | incubateAgain(); |
| 133 | } |
| 134 | |
| 135 | private: |
| 136 | QPointer<QSGRenderLoop> m_renderLoop; |
| 137 | int m_incubation_time; |
| 138 | int m_timer; |
| 139 | }; |
| 140 | |
| 141 | #if QT_CONFIG(accessibility) |
| 142 | /*! |
| 143 | Returns an accessibility interface for this window, or 0 if such an |
| 144 | interface cannot be created. |
| 145 | */ |
| 146 | QAccessibleInterface *QQuickWindow::accessibleRoot() const |
| 147 | { |
| 148 | return QAccessible::queryAccessibleInterface(const_cast<QQuickWindow*>(this)); |
| 149 | } |
| 150 | #endif |
| 151 | |
| 152 | |
| 153 | /* |
| 154 | Focus behavior |
| 155 | ============== |
| 156 | |
| 157 | Prior to being added to a valid window items can set and clear focus with no |
| 158 | effect. Only once items are added to a window (by way of having a parent set that |
| 159 | already belongs to a window) do the focus rules apply. Focus goes back to |
| 160 | having no effect if an item is removed from a window. |
| 161 | |
| 162 | When an item is moved into a new focus scope (either being added to a window |
| 163 | for the first time, or having its parent changed), if the focus scope already has |
| 164 | a scope focused item that takes precedence over the item being added. Otherwise, |
| 165 | the focus of the added tree is used. In the case of a tree of items being |
| 166 | added to a window for the first time, which may have a conflicted focus state (two |
| 167 | or more items in one scope having focus set), the same rule is applied item by item - |
| 168 | thus the first item that has focus will get it (assuming the scope doesn't already |
| 169 | have a scope focused item), and the other items will have their focus cleared. |
| 170 | */ |
| 171 | |
| 172 | QQuickRootItem::QQuickRootItem() |
| 173 | { |
| 174 | // child items with ItemObservesViewport can treat the window's content item |
| 175 | // as the ultimate viewport: avoid populating SG nodes that fall outside |
| 176 | setFlag(flag: ItemIsViewport); |
| 177 | } |
| 178 | |
| 179 | /*! \reimp */ |
| 180 | void QQuickWindow::exposeEvent(QExposeEvent *) |
| 181 | { |
| 182 | Q_D(QQuickWindow); |
| 183 | if (d->windowManager) |
| 184 | d->windowManager->exposureChanged(window: this); |
| 185 | } |
| 186 | |
| 187 | /*! \reimp */ |
| 188 | void QQuickWindow::resizeEvent(QResizeEvent *ev) |
| 189 | { |
| 190 | Q_D(QQuickWindow); |
| 191 | if (d->contentItem) |
| 192 | d->contentItem->setSize(ev->size()); |
| 193 | if (d->windowManager) |
| 194 | d->windowManager->resize(this); |
| 195 | } |
| 196 | |
| 197 | /*! \reimp */ |
| 198 | void QQuickWindow::showEvent(QShowEvent *) |
| 199 | { |
| 200 | Q_D(QQuickWindow); |
| 201 | if (d->windowManager) |
| 202 | d->windowManager->show(window: this); |
| 203 | } |
| 204 | |
| 205 | /*! \reimp */ |
| 206 | void QQuickWindow::hideEvent(QHideEvent *) |
| 207 | { |
| 208 | Q_D(QQuickWindow); |
| 209 | if (auto da = d->deliveryAgentPrivate()) |
| 210 | da->handleWindowHidden(win: this); |
| 211 | if (d->windowManager) |
| 212 | d->windowManager->hide(window: this); |
| 213 | } |
| 214 | |
| 215 | /*! \reimp */ |
| 216 | void QQuickWindow::closeEvent(QCloseEvent *e) |
| 217 | { |
| 218 | QQuickCloseEvent qev; |
| 219 | qev.setAccepted(e->isAccepted()); |
| 220 | emit closing(close: &qev); |
| 221 | e->setAccepted(qev.isAccepted()); |
| 222 | } |
| 223 | |
| 224 | /*! \reimp */ |
| 225 | void QQuickWindow::focusOutEvent(QFocusEvent *ev) |
| 226 | { |
| 227 | Q_D(QQuickWindow); |
| 228 | if (d->contentItem) |
| 229 | d->contentItem->setFocus(focus: false, reason: ev->reason()); |
| 230 | } |
| 231 | |
| 232 | /*! \reimp */ |
| 233 | void QQuickWindow::focusInEvent(QFocusEvent *ev) |
| 234 | { |
| 235 | Q_D(QQuickWindow); |
| 236 | if (d->inDestructor) |
| 237 | return; |
| 238 | if (d->contentItem) |
| 239 | d->contentItem->setFocus(focus: true, reason: ev->reason()); |
| 240 | if (auto da = d->deliveryAgentPrivate()) |
| 241 | da->updateFocusItemTransform(); |
| 242 | } |
| 243 | |
| 244 | #if QT_CONFIG(im) |
| 245 | static bool transformDirtyOnItemOrAncestor(const QQuickItem *item) |
| 246 | { |
| 247 | while (item) { |
| 248 | if (QQuickItemPrivate::get(item)->dirtyAttributes & ( |
| 249 | QQuickItemPrivate::TransformOrigin | |
| 250 | QQuickItemPrivate::Transform | |
| 251 | QQuickItemPrivate::BasicTransform | |
| 252 | QQuickItemPrivate::Position | |
| 253 | QQuickItemPrivate::Size | |
| 254 | QQuickItemPrivate::ParentChanged | |
| 255 | QQuickItemPrivate::Clip)) { |
| 256 | return true; |
| 257 | } |
| 258 | item = item->parentItem(); |
| 259 | } |
| 260 | return false; |
| 261 | } |
| 262 | #endif |
| 263 | |
| 264 | /*! |
| 265 | * \internal |
| 266 | |
| 267 | A "polish loop" can occur inside QQuickWindowPrivate::polishItems(). It is when an item calls |
| 268 | polish() on an(other?) item from updatePolish(). If this anomaly happens repeatedly and without |
| 269 | interruption (of a well-behaved updatePolish() that doesn't call polish()), it is a strong |
| 270 | indication that we are heading towards an infinite polish loop. A polish loop is not a bug in |
| 271 | Qt Quick - it is a bug caused by ill-behaved items put in the scene. |
| 272 | |
| 273 | We can detect this sequence of polish loops easily, since the |
| 274 | QQuickWindowPrivate::itemsToPolish is basically a stack: polish() will push to it, and |
| 275 | polishItems() will pop from it. |
| 276 | Therefore if updatePolish() calls polish(), the immediate next item polishItems() processes is |
| 277 | the item that was polished by the previous call to updatePolish(). |
| 278 | We therefore just need to count the number of polish loops we detected in _sequence_. |
| 279 | */ |
| 280 | struct PolishLoopDetector |
| 281 | { |
| 282 | PolishLoopDetector(const QVector<QQuickItem*> &itemsToPolish) |
| 283 | : itemsToPolish(itemsToPolish) |
| 284 | { |
| 285 | } |
| 286 | |
| 287 | /* |
| 288 | * returns true when it detected a likely infinite loop |
| 289 | * (suggests it should abort the polish loop) |
| 290 | **/ |
| 291 | bool check(QQuickItem *item, int itemsRemainingBeforeUpdatePolish) |
| 292 | { |
| 293 | if (itemsToPolish.size() > itemsRemainingBeforeUpdatePolish) { |
| 294 | // Detected potential polish loop. |
| 295 | ++numPolishLoopsInSequence; |
| 296 | if (numPolishLoopsInSequence == 10000) { |
| 297 | // We have looped 10,000 times without actually reducing the list of items to |
| 298 | // polish, give up for now. |
| 299 | // This is not a fix, just a remedy so that the application can be somewhat |
| 300 | // responsive. |
| 301 | numPolishLoopsInSequence = 0; |
| 302 | return true; |
| 303 | } |
| 304 | if (numPolishLoopsInSequence >= 1000 && numPolishLoopsInSequence < 1005) { |
| 305 | // Start to warn about polish loop after 1000 consecutive polish loops |
| 306 | // Show the 5 next items involved in the polish loop. |
| 307 | // (most likely they will be the same 5 items...) |
| 308 | QQuickItem *guiltyItem = itemsToPolish.last(); |
| 309 | qmlWarning(me: item) << "possible QQuickItem::polish() loop" ; |
| 310 | |
| 311 | auto typeAndObjectName = [](QQuickItem *item) { |
| 312 | QString typeName = QQmlMetaType::prettyTypeName(object: item); |
| 313 | QString objName = item->objectName(); |
| 314 | if (!objName.isNull()) |
| 315 | return QLatin1String("%1(%2)" ).arg(args&: typeName, args&: objName); |
| 316 | return typeName; |
| 317 | }; |
| 318 | |
| 319 | qmlWarning(me: guiltyItem) << typeAndObjectName(guiltyItem) |
| 320 | << " called polish() inside updatePolish() of " << typeAndObjectName(item); |
| 321 | } |
| 322 | } else { |
| 323 | numPolishLoopsInSequence = 0; |
| 324 | } |
| 325 | return false; |
| 326 | } |
| 327 | const QVector<QQuickItem*> &itemsToPolish; // Just a ref to the one in polishItems() |
| 328 | int numPolishLoopsInSequence = 0; |
| 329 | }; |
| 330 | |
| 331 | void QQuickWindowPrivate::polishItems() |
| 332 | { |
| 333 | // An item can trigger polish on another item, or itself for that matter, |
| 334 | // during its updatePolish() call. Because of this, we cannot simply |
| 335 | // iterate through the set, we must continue pulling items out until it |
| 336 | // is empty. |
| 337 | // In the case where polish is called from updatePolish() either directly |
| 338 | // or indirectly, we use a PolishLoopDetector to determine if a warning should |
| 339 | // be printed to the user. |
| 340 | |
| 341 | PolishLoopDetector polishLoopDetector(itemsToPolish); |
| 342 | while (!itemsToPolish.isEmpty()) { |
| 343 | QQuickItem *item = itemsToPolish.takeLast(); |
| 344 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
| 345 | itemPrivate->polishScheduled = false; |
| 346 | const int itemsRemaining = itemsToPolish.size(); |
| 347 | itemPrivate->updatePolish(); |
| 348 | item->updatePolish(); |
| 349 | if (polishLoopDetector.check(item, itemsRemainingBeforeUpdatePolish: itemsRemaining) == true) |
| 350 | break; |
| 351 | } |
| 352 | |
| 353 | #if QT_CONFIG(im) |
| 354 | if (QQuickItem *focusItem = q_func()->activeFocusItem()) { |
| 355 | // If the current focus item, or any of its anchestors, has changed location |
| 356 | // inside the window, we need inform IM about it. This to ensure that overlays |
| 357 | // such as selection handles will be updated. |
| 358 | const bool isActiveFocusItem = (focusItem == QGuiApplication::focusObject()); |
| 359 | const bool hasImEnabled = focusItem->inputMethodQuery(query: Qt::ImEnabled).toBool(); |
| 360 | if (isActiveFocusItem && hasImEnabled && transformDirtyOnItemOrAncestor(item: focusItem)) |
| 361 | deliveryAgentPrivate()->updateFocusItemTransform(); |
| 362 | } |
| 363 | #endif |
| 364 | |
| 365 | if (needsChildWindowStackingOrderUpdate) { |
| 366 | updateChildWindowStackingOrder(); |
| 367 | needsChildWindowStackingOrderUpdate = false; |
| 368 | } |
| 369 | } |
| 370 | |
| 371 | /*! |
| 372 | * Schedules the window to render another frame. |
| 373 | * |
| 374 | * Calling QQuickWindow::update() differs from QQuickItem::update() in that |
| 375 | * it always triggers a repaint, regardless of changes in the underlying |
| 376 | * scene graph or not. |
| 377 | */ |
| 378 | void QQuickWindow::update() |
| 379 | { |
| 380 | Q_D(QQuickWindow); |
| 381 | if (d->windowManager) |
| 382 | d->windowManager->update(window: this); |
| 383 | else if (d->renderControl) |
| 384 | QQuickRenderControlPrivate::get(renderControl: d->renderControl)->update(); |
| 385 | } |
| 386 | |
| 387 | static void updatePixelRatioHelper(QQuickItem *item, float pixelRatio) |
| 388 | { |
| 389 | if (item->flags() & QQuickItem::ItemHasContents) { |
| 390 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
| 391 | itemPrivate->itemChange(QQuickItem::ItemDevicePixelRatioHasChanged, pixelRatio); |
| 392 | } |
| 393 | |
| 394 | QList <QQuickItem *> items = item->childItems(); |
| 395 | for (int i = 0; i < items.size(); ++i) |
| 396 | updatePixelRatioHelper(item: items.at(i), pixelRatio); |
| 397 | } |
| 398 | |
| 399 | void QQuickWindow::physicalDpiChanged() |
| 400 | { |
| 401 | Q_D(QQuickWindow); |
| 402 | const qreal newPixelRatio = effectiveDevicePixelRatio(); |
| 403 | if (qFuzzyCompare(p1: newPixelRatio, p2: d->lastReportedItemDevicePixelRatio)) |
| 404 | return; |
| 405 | d->lastReportedItemDevicePixelRatio = newPixelRatio; |
| 406 | if (d->contentItem) |
| 407 | updatePixelRatioHelper(item: d->contentItem, pixelRatio: newPixelRatio); |
| 408 | d->forcePolish(); |
| 409 | } |
| 410 | |
| 411 | void QQuickWindow::handleFontDatabaseChanged() |
| 412 | { |
| 413 | Q_D(QQuickWindow); |
| 414 | d->pendingFontUpdate = true; |
| 415 | } |
| 416 | |
| 417 | void forcePolishHelper(QQuickItem *item) |
| 418 | { |
| 419 | if (item->flags() & QQuickItem::ItemHasContents) { |
| 420 | item->polish(); |
| 421 | } |
| 422 | |
| 423 | QList <QQuickItem *> items = item->childItems(); |
| 424 | for (int i=0; i<items.size(); ++i) |
| 425 | forcePolishHelper(item: items.at(i)); |
| 426 | } |
| 427 | |
| 428 | void QQuickWindow::handleScreenChanged(QScreen *screen) |
| 429 | { |
| 430 | Q_D(QQuickWindow); |
| 431 | Q_UNUSED(screen); |
| 432 | d->forcePolish(); |
| 433 | } |
| 434 | |
| 435 | /*! |
| 436 | Schedules polish events on all items in the scene. |
| 437 | */ |
| 438 | void QQuickWindowPrivate::forcePolish() |
| 439 | { |
| 440 | Q_Q(QQuickWindow); |
| 441 | if (!q->screen()) |
| 442 | return; |
| 443 | forcePolishHelper(item: contentItem); |
| 444 | } |
| 445 | |
| 446 | void forceUpdate(QQuickItem *item) |
| 447 | { |
| 448 | if (item->flags() & QQuickItem::ItemHasContents) |
| 449 | item->update(); |
| 450 | QQuickItemPrivate::get(item)->dirty(QQuickItemPrivate::ChildrenUpdateMask); |
| 451 | |
| 452 | QList <QQuickItem *> items = item->childItems(); |
| 453 | for (int i=0; i<items.size(); ++i) |
| 454 | forceUpdate(item: items.at(i)); |
| 455 | } |
| 456 | |
| 457 | void QQuickWindowRenderTarget::reset(QRhi *rhi, ResetFlags flags) |
| 458 | { |
| 459 | if (rhi) { |
| 460 | if (rt.owns) |
| 461 | delete rt.renderTarget; |
| 462 | |
| 463 | delete res.texture; |
| 464 | delete res.renderBuffer; |
| 465 | delete res.rpDesc; |
| 466 | } |
| 467 | |
| 468 | rt = {}; |
| 469 | res = {}; |
| 470 | |
| 471 | if (!flags.testFlag(flag: ResetFlag::KeepImplicitBuffers)) |
| 472 | implicitBuffers.reset(rhi); |
| 473 | |
| 474 | if (sw.owns) |
| 475 | delete sw.paintDevice; |
| 476 | |
| 477 | sw = {}; |
| 478 | } |
| 479 | |
| 480 | void QQuickWindowRenderTarget::ImplicitBuffers::reset(QRhi *rhi) |
| 481 | { |
| 482 | if (rhi) { |
| 483 | delete depthStencil; |
| 484 | delete depthStencilTexture; |
| 485 | delete multisampleTexture; |
| 486 | } |
| 487 | *this = {}; |
| 488 | } |
| 489 | |
| 490 | void QQuickWindowPrivate::invalidateFontData(QQuickItem *item) |
| 491 | { |
| 492 | QQuickTextInterface *textItem = qobject_cast<QQuickTextInterface *>(object: item); |
| 493 | if (textItem != nullptr) |
| 494 | textItem->invalidate(); |
| 495 | |
| 496 | QList<QQuickItem *> children = item->childItems(); |
| 497 | for (QQuickItem *child : children) |
| 498 | invalidateFontData(item: child); |
| 499 | } |
| 500 | |
| 501 | void QQuickWindowPrivate::ensureCustomRenderTarget() |
| 502 | { |
| 503 | // resolve() can be expensive when importing an existing native texture, so |
| 504 | // it is important to only do it when the QQuickRenderTarget was really changed. |
| 505 | if (!redirect.renderTargetDirty) |
| 506 | return; |
| 507 | |
| 508 | redirect.renderTargetDirty = false; |
| 509 | |
| 510 | redirect.rt.reset(rhi, flags: QQuickWindowRenderTarget::ResetFlag::KeepImplicitBuffers); |
| 511 | |
| 512 | if (!QQuickRenderTargetPrivate::get(rt: &customRenderTarget)->resolve(rhi, dst: &redirect.rt)) { |
| 513 | qWarning(msg: "Failed to set up render target redirection for QQuickWindow" ); |
| 514 | redirect.rt.reset(rhi); |
| 515 | } |
| 516 | } |
| 517 | |
| 518 | void QQuickWindowPrivate::setCustomCommandBuffer(QRhiCommandBuffer *cb) |
| 519 | { |
| 520 | // ownership not transferred |
| 521 | redirect.commandBuffer = cb; |
| 522 | } |
| 523 | |
| 524 | void QQuickWindowPrivate::syncSceneGraph() |
| 525 | { |
| 526 | Q_Q(QQuickWindow); |
| 527 | |
| 528 | const bool wasRtDirty = redirect.renderTargetDirty; |
| 529 | ensureCustomRenderTarget(); |
| 530 | |
| 531 | QRhiCommandBuffer *cb = nullptr; |
| 532 | if (rhi) { |
| 533 | if (redirect.commandBuffer) |
| 534 | cb = redirect.commandBuffer; |
| 535 | else |
| 536 | cb = swapchain->currentFrameCommandBuffer(); |
| 537 | } |
| 538 | context->prepareSync(devicePixelRatio: q->effectiveDevicePixelRatio(), cb, config: graphicsConfig); |
| 539 | |
| 540 | animationController->beforeNodeSync(); |
| 541 | |
| 542 | emit q->beforeSynchronizing(); |
| 543 | runAndClearJobs(jobs: &beforeSynchronizingJobs); |
| 544 | |
| 545 | if (pendingFontUpdate) { |
| 546 | QFont::cleanup(); |
| 547 | invalidateFontData(item: contentItem); |
| 548 | } |
| 549 | |
| 550 | if (Q_UNLIKELY(!renderer)) { |
| 551 | forceUpdate(item: contentItem); |
| 552 | |
| 553 | QSGRootNode *rootNode = new QSGRootNode; |
| 554 | rootNode->appendChildNode(node: QQuickItemPrivate::get(item: contentItem)->itemNode()); |
| 555 | const bool useDepth = graphicsConfig.isDepthBufferEnabledFor2D(); |
| 556 | const QSGRendererInterface::RenderMode renderMode = useDepth ? QSGRendererInterface::RenderMode2D |
| 557 | : QSGRendererInterface::RenderMode2DNoDepthBuffer; |
| 558 | renderer = context->createRenderer(renderMode); |
| 559 | renderer->setRootNode(rootNode); |
| 560 | } else if (Q_UNLIKELY(wasRtDirty) |
| 561 | && q->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) { |
| 562 | auto softwareRenderer = static_cast<QSGSoftwareRenderer *>(renderer); |
| 563 | softwareRenderer->markDirty(); |
| 564 | } |
| 565 | |
| 566 | updateDirtyNodes(); |
| 567 | |
| 568 | animationController->afterNodeSync(); |
| 569 | |
| 570 | renderer->setClearColor(clearColor); |
| 571 | |
| 572 | renderer->setVisualizationMode(visualizationMode); |
| 573 | |
| 574 | if (pendingFontUpdate) { |
| 575 | context->invalidateGlyphCaches(); |
| 576 | pendingFontUpdate = false; |
| 577 | } |
| 578 | |
| 579 | emit q->afterSynchronizing(); |
| 580 | runAndClearJobs(jobs: &afterSynchronizingJobs); |
| 581 | } |
| 582 | |
| 583 | void QQuickWindowPrivate::emitBeforeRenderPassRecording(void *ud) |
| 584 | { |
| 585 | QQuickWindow *w = reinterpret_cast<QQuickWindow *>(ud); |
| 586 | emit w->beforeRenderPassRecording(); |
| 587 | } |
| 588 | |
| 589 | void QQuickWindowPrivate::emitAfterRenderPassRecording(void *ud) |
| 590 | { |
| 591 | QQuickWindow *w = reinterpret_cast<QQuickWindow *>(ud); |
| 592 | emit w->afterRenderPassRecording(); |
| 593 | } |
| 594 | |
| 595 | int QQuickWindowPrivate::multiViewCount() |
| 596 | { |
| 597 | if (rhi) { |
| 598 | ensureCustomRenderTarget(); |
| 599 | if (redirect.rt.rt.renderTarget) |
| 600 | return redirect.rt.rt.multiViewCount; |
| 601 | } |
| 602 | |
| 603 | // Note that on QRhi level 0 and 1 are often used interchangeably, as both mean |
| 604 | // no-multiview. Here in Qt Quick let's always use 1 as the default |
| 605 | // (no-multiview), so that higher layers (effects, materials) do not need to |
| 606 | // handle both 0 and 1, only 1. |
| 607 | return 1; |
| 608 | } |
| 609 | |
| 610 | QRhiRenderTarget *QQuickWindowPrivate::activeCustomRhiRenderTarget() |
| 611 | { |
| 612 | if (rhi) { |
| 613 | ensureCustomRenderTarget(); |
| 614 | return redirect.rt.rt.renderTarget; |
| 615 | } |
| 616 | return nullptr; |
| 617 | } |
| 618 | |
| 619 | void QQuickWindowPrivate::renderSceneGraph() |
| 620 | { |
| 621 | Q_Q(QQuickWindow); |
| 622 | if (!renderer) |
| 623 | return; |
| 624 | |
| 625 | ensureCustomRenderTarget(); |
| 626 | |
| 627 | QSGRenderTarget sgRenderTarget; |
| 628 | if (rhi) { |
| 629 | QRhiRenderTarget *rt; |
| 630 | QRhiRenderPassDescriptor *rp; |
| 631 | QRhiCommandBuffer *cb; |
| 632 | if (redirect.rt.rt.renderTarget) { |
| 633 | rt = redirect.rt.rt.renderTarget; |
| 634 | rp = rt->renderPassDescriptor(); |
| 635 | if (!rp) { |
| 636 | qWarning(msg: "Custom render target is set but no renderpass descriptor has been provided." ); |
| 637 | return; |
| 638 | } |
| 639 | cb = redirect.commandBuffer; |
| 640 | if (!cb) { |
| 641 | qWarning(msg: "Custom render target is set but no command buffer has been provided." ); |
| 642 | return; |
| 643 | } |
| 644 | } else { |
| 645 | if (!swapchain) { |
| 646 | qWarning(msg: "QQuickWindow: No render target (neither swapchain nor custom target was provided)" ); |
| 647 | return; |
| 648 | } |
| 649 | rt = swapchain->currentFrameRenderTarget(); |
| 650 | rp = rpDescForSwapchain; |
| 651 | cb = swapchain->currentFrameCommandBuffer(); |
| 652 | } |
| 653 | sgRenderTarget = QSGRenderTarget(rt, rp, cb); |
| 654 | sgRenderTarget.multiViewCount = multiViewCount(); |
| 655 | } else { |
| 656 | sgRenderTarget = QSGRenderTarget(redirect.rt.sw.paintDevice); |
| 657 | } |
| 658 | |
| 659 | context->beginNextFrame(renderer, |
| 660 | renderTarget: sgRenderTarget, |
| 661 | mainPassRecordingStart: emitBeforeRenderPassRecording, |
| 662 | mainPassRecordingEnd: emitAfterRenderPassRecording, |
| 663 | callbackUserData: q); |
| 664 | |
| 665 | animationController->advance(); |
| 666 | emit q->beforeRendering(); |
| 667 | runAndClearJobs(jobs: &beforeRenderingJobs); |
| 668 | |
| 669 | const qreal devicePixelRatio = q->effectiveDevicePixelRatio(); |
| 670 | QSize pixelSize; |
| 671 | if (redirect.rt.rt.renderTarget) |
| 672 | pixelSize = redirect.rt.rt.renderTarget->pixelSize(); |
| 673 | else if (redirect.rt.sw.paintDevice) |
| 674 | pixelSize = QSize(redirect.rt.sw.paintDevice->width(), redirect.rt.sw.paintDevice->height()); |
| 675 | else if (rhi) |
| 676 | pixelSize = swapchain->currentPixelSize(); |
| 677 | else // software or other backend |
| 678 | pixelSize = q->size() * devicePixelRatio; |
| 679 | |
| 680 | renderer->setDevicePixelRatio(devicePixelRatio); |
| 681 | renderer->setDeviceRect(QRect(QPoint(0, 0), pixelSize)); |
| 682 | renderer->setViewportRect(QRect(QPoint(0, 0), pixelSize)); |
| 683 | |
| 684 | QSGAbstractRenderer::MatrixTransformFlags matrixFlags; |
| 685 | bool flipY = rhi ? !rhi->isYUpInNDC() : false; |
| 686 | if (!customRenderTarget.isNull() && customRenderTarget.mirrorVertically()) |
| 687 | flipY = !flipY; |
| 688 | if (flipY) |
| 689 | matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY; |
| 690 | |
| 691 | const QRectF rect(QPointF(0, 0), pixelSize / devicePixelRatio); |
| 692 | renderer->setProjectionMatrixToRect(rect, flags: matrixFlags, nativeNDCFlipY: rhi && !rhi->isYUpInNDC()); |
| 693 | |
| 694 | context->renderNextFrame(renderer); |
| 695 | |
| 696 | emit q->afterRendering(); |
| 697 | runAndClearJobs(jobs: &afterRenderingJobs); |
| 698 | |
| 699 | context->endNextFrame(renderer); |
| 700 | |
| 701 | if (renderer && renderer->hasVisualizationModeWithContinuousUpdate()) { |
| 702 | // For the overdraw visualizer. This update is not urgent so avoid a |
| 703 | // direct update() call, this is only here to keep the overdraw |
| 704 | // visualization box rotating even when the scene is static. |
| 705 | QCoreApplication::postEvent(receiver: q, event: new QEvent(QEvent::Type(FullUpdateRequest))); |
| 706 | } |
| 707 | } |
| 708 | |
| 709 | QQuickWindowPrivate::QQuickWindowPrivate() |
| 710 | : contentItem(nullptr) |
| 711 | , dirtyItemList(nullptr) |
| 712 | , lastReportedItemDevicePixelRatio(0) |
| 713 | , context(nullptr) |
| 714 | , renderer(nullptr) |
| 715 | , windowManager(nullptr) |
| 716 | , renderControl(nullptr) |
| 717 | , clearColor(Qt::white) |
| 718 | , persistentGraphics(true) |
| 719 | , persistentSceneGraph(true) |
| 720 | , inDestructor(false) |
| 721 | , incubationController(nullptr) |
| 722 | , hasActiveSwapchain(false) |
| 723 | , hasRenderableSwapchain(false) |
| 724 | , swapchainJustBecameRenderable(false) |
| 725 | , updatesEnabled(true) |
| 726 | { |
| 727 | } |
| 728 | |
| 729 | QQuickWindowPrivate::~QQuickWindowPrivate() |
| 730 | { |
| 731 | inDestructor = true; |
| 732 | redirect.rt.reset(rhi); |
| 733 | if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>()) |
| 734 | service->removeWindow(q_func()); |
| 735 | deliveryAgent = nullptr; |
| 736 | } |
| 737 | |
| 738 | void QQuickWindowPrivate::setPalette(QQuickPalette* palette) |
| 739 | { |
| 740 | if (windowPaletteRef == palette) |
| 741 | return; |
| 742 | |
| 743 | if (windowPaletteRef) |
| 744 | disconnect(sender: windowPaletteRef, signal: &QQuickPalette::changed, receiverPrivate: this, slot: &QQuickWindowPrivate::updateWindowPalette); |
| 745 | windowPaletteRef = palette; |
| 746 | updateWindowPalette(); |
| 747 | if (windowPaletteRef) |
| 748 | connect(sender: windowPaletteRef, signal: &QQuickPalette::changed, receiverPrivate: this, slot: &QQuickWindowPrivate::updateWindowPalette); |
| 749 | } |
| 750 | |
| 751 | void QQuickWindowPrivate::updateWindowPalette() |
| 752 | { |
| 753 | QQuickPaletteProviderPrivateBase::setPalette(windowPaletteRef); |
| 754 | } |
| 755 | |
| 756 | void QQuickWindowPrivate::updateChildrenPalettes(const QPalette &parentPalette) |
| 757 | { |
| 758 | Q_Q(QQuickWindow); |
| 759 | if (auto root = q->contentItem()) { |
| 760 | for (auto &&child: root->childItems()) { |
| 761 | QQuickItemPrivate::get(item: child)->inheritPalette(parentPalette); |
| 762 | } |
| 763 | } |
| 764 | } |
| 765 | |
| 766 | void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control) |
| 767 | { |
| 768 | q_ptr = c; |
| 769 | |
| 770 | |
| 771 | Q_Q(QQuickWindow); |
| 772 | |
| 773 | contentItem = new QQuickRootItem; |
| 774 | contentItem->setObjectName(q->objectName()); |
| 775 | QQml_setParent_noEvent(object: contentItem, parent: c); |
| 776 | QQmlEngine::setObjectOwnership(contentItem, QQmlEngine::CppOwnership); |
| 777 | QQuickItemPrivate *contentItemPrivate = QQuickItemPrivate::get(item: contentItem); |
| 778 | contentItemPrivate->window = q; |
| 779 | contentItemPrivate->windowRefCount = 1; |
| 780 | contentItemPrivate->flags |= QQuickItem::ItemIsFocusScope; |
| 781 | contentItem->setSize(q->size()); |
| 782 | deliveryAgent = new QQuickDeliveryAgent(contentItem); |
| 783 | |
| 784 | visualizationMode = qgetenv(varName: "QSG_VISUALIZE" ); |
| 785 | renderControl = control; |
| 786 | if (renderControl) |
| 787 | QQuickRenderControlPrivate::get(renderControl)->window = q; |
| 788 | |
| 789 | if (!renderControl) |
| 790 | windowManager = QSGRenderLoop::instance(); |
| 791 | |
| 792 | Q_ASSERT(windowManager || renderControl); |
| 793 | |
| 794 | QObject::connect(sender: static_cast<QGuiApplication *>(QGuiApplication::instance()), |
| 795 | signal: &QGuiApplication::fontDatabaseChanged, |
| 796 | context: q, |
| 797 | slot: &QQuickWindow::handleFontDatabaseChanged); |
| 798 | |
| 799 | if (q->screen()) { |
| 800 | lastReportedItemDevicePixelRatio = q->effectiveDevicePixelRatio(); |
| 801 | } |
| 802 | |
| 803 | QSGContext *sg; |
| 804 | if (renderControl) { |
| 805 | QQuickRenderControlPrivate *renderControlPriv = QQuickRenderControlPrivate::get(renderControl); |
| 806 | sg = renderControlPriv->sg; |
| 807 | context = renderControlPriv->rc; |
| 808 | } else { |
| 809 | windowManager->addWindow(win: q); |
| 810 | sg = windowManager->sceneGraphContext(); |
| 811 | context = windowManager->createRenderContext(sg); |
| 812 | } |
| 813 | |
| 814 | q->setSurfaceType(windowManager ? windowManager->windowSurfaceType() : QSurface::OpenGLSurface); |
| 815 | q->setFormat(sg->defaultSurfaceFormat()); |
| 816 | // When using Vulkan, associating a scenegraph-managed QVulkanInstance with |
| 817 | // the window (but only when not using renderControl) is deferred to |
| 818 | // QSGRhiSupport::createRhi(). This allows applications to set up their own |
| 819 | // QVulkanInstance and set that on the window, if they wish to. |
| 820 | |
| 821 | animationController.reset(other: new QQuickAnimatorController(q)); |
| 822 | |
| 823 | QObject::connect(sender: context, signal: &QSGRenderContext::initialized, context: q, slot: &QQuickWindow::sceneGraphInitialized, type: Qt::DirectConnection); |
| 824 | QObject::connect(sender: context, signal: &QSGRenderContext::invalidated, context: q, slot: &QQuickWindow::sceneGraphInvalidated, type: Qt::DirectConnection); |
| 825 | QObject::connect(sender: context, signal: &QSGRenderContext::invalidated, context: q, slot: &QQuickWindow::cleanupSceneGraph, type: Qt::DirectConnection); |
| 826 | |
| 827 | QObject::connect(sender: q, signal: &QQuickWindow::focusObjectChanged, context: q, slot: &QQuickWindow::activeFocusItemChanged); |
| 828 | QObject::connect(sender: q, signal: &QQuickWindow::screenChanged, context: q, slot: &QQuickWindow::handleScreenChanged); |
| 829 | QObject::connect(qApp, signal: &QGuiApplication::applicationStateChanged, context: q, slot: &QQuickWindow::handleApplicationStateChanged); |
| 830 | QObject::connect(sender: q, signal: &QQuickWindow::frameSwapped, context: q, slot: &QQuickWindow::runJobsAfterSwap, type: Qt::DirectConnection); |
| 831 | |
| 832 | if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>()) |
| 833 | service->addWindow(q); |
| 834 | } |
| 835 | |
| 836 | void QQuickWindow::handleApplicationStateChanged(Qt::ApplicationState state) |
| 837 | { |
| 838 | Q_D(QQuickWindow); |
| 839 | if (state != Qt::ApplicationActive && d->contentItem) { |
| 840 | auto da = d->deliveryAgentPrivate(); |
| 841 | Q_ASSERT(da); |
| 842 | da->handleWindowDeactivate(win: this); |
| 843 | } |
| 844 | } |
| 845 | |
| 846 | /*! |
| 847 | \property QQuickWindow::data |
| 848 | \internal |
| 849 | */ |
| 850 | |
| 851 | QQmlListProperty<QObject> QQuickWindowPrivate::data() |
| 852 | { |
| 853 | QQmlListProperty<QObject> ret; |
| 854 | |
| 855 | ret.object = q_func(); |
| 856 | ret.append = QQuickWindowPrivate::data_append; |
| 857 | ret.count = QQuickWindowPrivate::data_count; |
| 858 | ret.at = QQuickWindowPrivate::data_at; |
| 859 | ret.clear = QQuickWindowPrivate::data_clear; |
| 860 | // replace is not supported by QQuickItem. Don't synthesize it. |
| 861 | ret.removeLast = QQuickWindowPrivate::data_removeLast; |
| 862 | |
| 863 | return ret; |
| 864 | } |
| 865 | |
| 866 | void QQuickWindowPrivate::dirtyItem(QQuickItem *item) |
| 867 | { |
| 868 | Q_Q(QQuickWindow); |
| 869 | |
| 870 | QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); |
| 871 | if (itemPriv->dirtyAttributes & QQuickItemPrivate::ChildrenStackingChanged) |
| 872 | needsChildWindowStackingOrderUpdate = true; |
| 873 | |
| 874 | q->maybeUpdate(); |
| 875 | } |
| 876 | |
| 877 | /*! |
| 878 | \deprecated Use QPointerEvent::exclusiveGrabber(). |
| 879 | Returns the item which currently has the mouse grab. |
| 880 | */ |
| 881 | QQuickItem *QQuickWindow::mouseGrabberItem() const |
| 882 | { |
| 883 | Q_D(const QQuickWindow); |
| 884 | auto da = const_cast<QQuickWindowPrivate *>(d)->deliveryAgentPrivate(); |
| 885 | Q_ASSERT(da); |
| 886 | // The normal use case is to call this function while an event is being delivered; |
| 887 | // but if the caller knows about the event, it should call QPointerEvent::exclusiveGrabber() instead. |
| 888 | if (auto epd = da->mousePointData()) |
| 889 | return qmlobject_cast<QQuickItem *>(object: epd->exclusiveGrabber); |
| 890 | |
| 891 | if (Q_LIKELY(d->deliveryAgentPrivate()->eventsInDelivery.isEmpty())) |
| 892 | // mousePointData() checked that already: it's one reason epd can be null |
| 893 | qCDebug(lcMouse, "mouse grabber ambiguous: no event is currently being delivered" ); |
| 894 | // If no event is being delivered, we can return "the mouse" grabber, |
| 895 | // but in general there could be more than one mouse, could be only a touchscreen etc. |
| 896 | // That's why this function is obsolete. |
| 897 | return qmlobject_cast<QQuickItem *>(object: QPointingDevicePrivate::get(q: QPointingDevice::primaryPointingDevice())-> |
| 898 | firstPointExclusiveGrabber()); |
| 899 | } |
| 900 | |
| 901 | void QQuickWindowPrivate::cleanup(QSGNode *n) |
| 902 | { |
| 903 | Q_Q(QQuickWindow); |
| 904 | |
| 905 | Q_ASSERT(!cleanupNodeList.contains(n)); |
| 906 | cleanupNodeList.append(t: n); |
| 907 | q->maybeUpdate(); |
| 908 | } |
| 909 | |
| 910 | /*! |
| 911 | \qmltype Window |
| 912 | \nativetype QQuickWindow |
| 913 | \inqmlmodule QtQuick |
| 914 | \ingroup qtquick-visual |
| 915 | \brief Creates a new top-level window. |
| 916 | |
| 917 | The Window object creates a new top-level window for a Qt Quick scene. It automatically sets up the |
| 918 | window for use with \c {QtQuick} graphical types. |
| 919 | |
| 920 | A Window can be declared inside an Item or inside another Window, in which |
| 921 | case the inner Window will automatically become "transient for" the outer |
| 922 | Window, with the outer Window as its \l transientParent. Most platforms will |
| 923 | show the Window centered upon the outer window in this case, and there may be |
| 924 | other platform-dependent behaviors, depending also on the \l flags. If the nested |
| 925 | window is intended to be a dialog in your application, you should also set \l flags |
| 926 | to \c Qt.Dialog, because some window managers will not provide the centering behavior |
| 927 | without that flag. |
| 928 | |
| 929 | You can also declare multiple windows inside a top-level \l QtObject, in which |
| 930 | case the windows will have no transient relationship. |
| 931 | |
| 932 | Alternatively you can set or bind \l x and \l y to position the Window |
| 933 | explicitly on the screen. |
| 934 | |
| 935 | When the user attempts to close a window, the \l closing signal will be |
| 936 | emitted. You can force the window to stay open (for example to prompt the |
| 937 | user to save changes) by writing an \c onClosing handler that sets |
| 938 | \c {close.accepted = false} unless it's safe to close the window (for example, |
| 939 | because there are no more unsaved changes). |
| 940 | |
| 941 | \code |
| 942 | onClosing: (close) => { |
| 943 | if (document.changed) { |
| 944 | close.accepted = false |
| 945 | confirmExitPopup.open() |
| 946 | } |
| 947 | } |
| 948 | |
| 949 | // The confirmExitPopup allows user to save or discard the document, |
| 950 | // or to cancel the closing. |
| 951 | \endcode |
| 952 | |
| 953 | \section1 Styling |
| 954 | |
| 955 | As with all visual types in Qt Quick, Window supports |
| 956 | \l {palette}{palettes}. However, as with types like \l Text, Window does |
| 957 | not use palettes by default. For example, to change the background color |
| 958 | of the window when the operating system's theme changes, the \l color must |
| 959 | be set: |
| 960 | |
| 961 | \snippet qml/windowPalette.qml declaration-and-color |
| 962 | \codeline |
| 963 | \snippet qml/windowPalette.qml text-item |
| 964 | \snippet qml/windowPalette.qml closing-brace |
| 965 | |
| 966 | Use \l {ApplicationWindow} (and \l {Label}) from \l {Qt Quick Controls} |
| 967 | instead of Window to get automatic styling. |
| 968 | */ |
| 969 | |
| 970 | /*! |
| 971 | \class QQuickWindow |
| 972 | \since 5.0 |
| 973 | |
| 974 | \inmodule QtQuick |
| 975 | |
| 976 | \brief The QQuickWindow class provides the window for displaying a graphical QML scene. |
| 977 | |
| 978 | QQuickWindow provides the graphical scene management needed to interact with and display |
| 979 | a scene of QQuickItems. |
| 980 | |
| 981 | A QQuickWindow always has a single invisible root item. To add items to this window, |
| 982 | reparent the items to the root item or to an existing item in the scene. |
| 983 | |
| 984 | For easily displaying a scene from a QML file, see \l{QQuickView}. |
| 985 | |
| 986 | \section1 Rendering |
| 987 | |
| 988 | QQuickWindow uses a scene graph to represent what needs to be rendered. |
| 989 | This scene graph is disconnected from the QML scene and potentially lives in |
| 990 | another thread, depending on the platform implementation. Since the |
| 991 | rendering scene graph lives independently from the QML scene, it can also be |
| 992 | completely released without affecting the state of the QML scene. |
| 993 | |
| 994 | The sceneGraphInitialized() signal is emitted on the rendering thread before |
| 995 | the QML scene is rendered to the screen for the first time. If the rendering |
| 996 | scene graph has been released, the signal will be emitted again before the |
| 997 | next frame is rendered. A visible, on-screen QQuickWindow is driven |
| 998 | internally by a \c{render loop}, of which there are multiple implementations |
| 999 | provided in the scene graph. For details on the scene graph rendering |
| 1000 | process, see \l{Qt Quick Scene Graph}. |
| 1001 | |
| 1002 | By default, a QQuickWindow renders using an accelerated 3D graphics API, |
| 1003 | such as OpenGL or Vulkan. See \l{Scene Graph Adaptations} for a detailed |
| 1004 | overview of scene graph backends and the supported graphics APIs. |
| 1005 | |
| 1006 | \warning It is crucial that graphics operations and interaction with the |
| 1007 | scene graph happens exclusively on the rendering thread, primarily during |
| 1008 | the updatePaintNode() phase. |
| 1009 | |
| 1010 | \warning As many of the signals related to rendering are emitted from the |
| 1011 | rendering thread, connections should be made using Qt::DirectConnection. |
| 1012 | |
| 1013 | \section2 Integration with Accelerated 3D Graphics APIs |
| 1014 | |
| 1015 | It is possible to integrate OpenGL, Vulkan, Metal, or Direct3D 11 calls |
| 1016 | directly into the QQuickWindow, as long as the QQuickWindow and the |
| 1017 | underlying scene graph is rendering using the same API. To access native |
| 1018 | graphics objects, such as device or context object handles, use |
| 1019 | QSGRendererInterface. An instance of QSGRendererInterface is queriable from |
| 1020 | QQuickWindow by calling rendererInterface(). The enablers for this |
| 1021 | integration are the beforeRendering(), beforeRenderPassRecording(), |
| 1022 | afterRenderPassRecording(), and related signals. These allow rendering |
| 1023 | underlays or overlays. Alternatively, QNativeInterface::QSGOpenGLTexture, |
| 1024 | QNativeInterface::QSGVulkanTexture, and other similar classes allow |
| 1025 | wrapping an existing native texture or image object in a QSGTexture that |
| 1026 | can then be used with the scene graph. |
| 1027 | |
| 1028 | \section2 Rendering without Acceleration |
| 1029 | |
| 1030 | A limited, pure software based rendering path is available as well. With the |
| 1031 | \c software backend, a number of Qt Quick features are not available, QML |
| 1032 | items relying on these will not be rendered at all. At the same time, this |
| 1033 | allows QQuickWindow to be functional even on systems where there is no 3D |
| 1034 | graphics API available at all. See \l{Qt Quick Software Adaptation} for more |
| 1035 | details. |
| 1036 | |
| 1037 | \section2 Redirected Rendering |
| 1038 | |
| 1039 | A QQuickWindow is not necessarily backed by a native window on screen. The |
| 1040 | rendering can be redirected to target a custom render target, such as a |
| 1041 | given native texture. This is achieved in combination with the |
| 1042 | QQuickRenderControl class, and functions such as setRenderTarget(), |
| 1043 | setGraphicsDevice(), and setGraphicsConfiguration(). |
| 1044 | |
| 1045 | In this case, the QQuickWindow represents the scene, and provides the |
| 1046 | intrastructure for rendering a frame. It will not be backed by a render |
| 1047 | loop and a native window. Instead, in this case the application drives |
| 1048 | rendering, effectively substituting for the render loops. This allows |
| 1049 | generating image sequences, rendering into textures for use in external 3D |
| 1050 | engines, or rendering Qt Quick content within a VR environment. |
| 1051 | |
| 1052 | \section2 Resource Management |
| 1053 | |
| 1054 | QML will try to cache images and scene graph nodes to improve performance, |
| 1055 | but in some low-memory scenarios it might be required to aggressively |
| 1056 | release these resources. The releaseResources() function can be used to |
| 1057 | force the clean up of certain resources, especially resource that are cached |
| 1058 | and can be recreated later when needed again. |
| 1059 | |
| 1060 | Additionally, calling releaseResources() may result in releasing the entire |
| 1061 | scene graph and the associated graphics resources. The |
| 1062 | sceneGraphInvalidated() signal will be emitted when this happens. This |
| 1063 | behavior is controlled by the setPersistentGraphics() and |
| 1064 | setPersistentSceneGraph() functions. |
| 1065 | |
| 1066 | \note All classes with QSG prefix should be used solely on the scene graph's |
| 1067 | rendering thread. See \l {Scene Graph and Rendering} for more information. |
| 1068 | |
| 1069 | \section2 Exposure and Visibility |
| 1070 | |
| 1071 | When a QQuickWindow instance is deliberately hidden with hide() or |
| 1072 | setVisible(false), it will stop rendering and its scene graph and graphics |
| 1073 | context might be released as well. This depends on the settings configured |
| 1074 | by setPersistentGraphics() and setPersistentSceneGraph(). The behavior in |
| 1075 | this respect is identical to explicitly calling the releaseResources() |
| 1076 | function. A window can become not exposed, in other words non-renderable, by |
| 1077 | other means as well. This depends on the platform and windowing system. For |
| 1078 | example, on Windows minimizing a window makes it stop rendering. On \macos |
| 1079 | fully obscuring a window by other windows on top triggers the same. On |
| 1080 | Linux/X11, the behavior is dependent on the window manager. |
| 1081 | |
| 1082 | \section2 OpenGL Context and Surface Formats |
| 1083 | |
| 1084 | While it is possible to specify a QSurfaceFormat for every QQuickWindow by |
| 1085 | calling the member function setFormat(), windows may also be created from |
| 1086 | QML by using the Window and ApplicationWindow elements. In this case there |
| 1087 | is no C++ code involved in the creation of the window instance, yet |
| 1088 | applications may still wish to set certain surface format values, for |
| 1089 | example to request a given OpenGL version or profile. Such applications can |
| 1090 | call the static function QSurfaceFormat::setDefaultFormat() at startup. The |
| 1091 | specified format will be used for all Quick windows created afterwards. |
| 1092 | |
| 1093 | \section2 Vulkan Instance |
| 1094 | |
| 1095 | When using Vulkan, a QQuickWindow is automatically associated with a |
| 1096 | QVulkanInstance that is created and managed internally by the scene graph. |
| 1097 | This way most applications do not need to worry about having a \c |
| 1098 | VkInstance available since it all happens automatically. In advanced cases |
| 1099 | an application may wish to create its own QVulkanInstance, in order to |
| 1100 | configure it in a specific way. That is possible as well. Calling |
| 1101 | \l{QWindow::setVulkanInstance()}{setVulkanInstance()} on the QQuickWindow |
| 1102 | right after construction, before making it visible, leads to using the |
| 1103 | application-supplied QVulkanInstance (and the underlying \c VkInstance). |
| 1104 | When redirecting via QQuickRenderControl, there is no QVulkanInstance |
| 1105 | provided automatically, but rather the application is expected to provide |
| 1106 | its own and associate it with the QQuickWindow. |
| 1107 | |
| 1108 | \section2 Graphics Contexts and Devices |
| 1109 | |
| 1110 | When the scene graph is initialized, which typically happens when the |
| 1111 | window becomes exposed or, in case of redirected rendering, initialization |
| 1112 | is performed \l{QQuickRenderControl::initialize()}{via |
| 1113 | QQuickRenderControl}, the context or device objects necessary for rendering |
| 1114 | are created automatically. This includes OpenGL contexts, Direct3D devices |
| 1115 | and device contexts, Vulkan and Metal devices. These are also queriable by |
| 1116 | application code afterwards via |
| 1117 | \l{QSGRendererInterface::getResource()}{QSGRendererInterface}. When using |
| 1118 | the \c basic render loop, which performs all rendering on the GUI thread, |
| 1119 | the same context or device is used with all visible QQuickWindows. The \c |
| 1120 | threaded render loop uses a dedicated context or device object for each |
| 1121 | rendering thread, and so for each QQuickWindow. With some graphics APIs, |
| 1122 | there is a degree of customizability provided via |
| 1123 | setGraphicsConfiguration(). This makes it possible, for example, to specify |
| 1124 | the list of Vulkan extensions to enable on the \c VkDevice. Alternatively, |
| 1125 | it is also possible to provide a set of existing context or device objects |
| 1126 | for use by the QQuickWindow, instead of letting it construct its own. This |
| 1127 | is achieved through setGraphicsDevice(). |
| 1128 | |
| 1129 | \sa QQuickView, QQuickRenderControl, QQuickRenderTarget, |
| 1130 | QQuickGraphicsDevice, QQuickGraphicsConfiguration, QSGRendererInterface |
| 1131 | */ |
| 1132 | |
| 1133 | /*! |
| 1134 | Constructs a window for displaying a QML scene with parent window \a parent. |
| 1135 | */ |
| 1136 | QQuickWindow::QQuickWindow(QWindow *parent) |
| 1137 | : QQuickWindow(*new QQuickWindowPrivate, parent) |
| 1138 | { |
| 1139 | } |
| 1140 | |
| 1141 | |
| 1142 | |
| 1143 | /*! |
| 1144 | \internal |
| 1145 | */ |
| 1146 | QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QWindow *parent) |
| 1147 | : QWindow(dd, parent) |
| 1148 | { |
| 1149 | Q_D(QQuickWindow); |
| 1150 | d->init(c: this); |
| 1151 | } |
| 1152 | |
| 1153 | /*! |
| 1154 | Constructs a window for displaying a QML scene, whose rendering will |
| 1155 | be controlled by the \a control object. |
| 1156 | Please refer to QQuickRenderControl's documentation for more information. |
| 1157 | |
| 1158 | \since 5.4 |
| 1159 | */ |
| 1160 | QQuickWindow::QQuickWindow(QQuickRenderControl *control) |
| 1161 | : QWindow(*(new QQuickWindowPrivate), nullptr) |
| 1162 | { |
| 1163 | Q_D(QQuickWindow); |
| 1164 | d->init(c: this, control); |
| 1165 | } |
| 1166 | |
| 1167 | /*! |
| 1168 | \internal |
| 1169 | */ |
| 1170 | QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QQuickRenderControl *control) |
| 1171 | : QWindow(dd, nullptr) |
| 1172 | { |
| 1173 | Q_D(QQuickWindow); |
| 1174 | d->init(c: this, control); |
| 1175 | } |
| 1176 | |
| 1177 | /*! |
| 1178 | Destroys the window. |
| 1179 | */ |
| 1180 | QQuickWindow::~QQuickWindow() |
| 1181 | { |
| 1182 | Q_D(QQuickWindow); |
| 1183 | d->inDestructor = true; |
| 1184 | if (d->renderControl) { |
| 1185 | QQuickRenderControlPrivate::get(renderControl: d->renderControl)->windowDestroyed(); |
| 1186 | } else if (d->windowManager) { |
| 1187 | d->windowManager->removeWindow(win: this); |
| 1188 | d->windowManager->windowDestroyed(window: this); |
| 1189 | } |
| 1190 | |
| 1191 | delete d->incubationController; d->incubationController = nullptr; |
| 1192 | QQuickRootItem *root = d->contentItem; |
| 1193 | d->contentItem = nullptr; |
| 1194 | root->setParent(nullptr); // avoid QChildEvent delivery during deletion |
| 1195 | delete root; |
| 1196 | d->deliveryAgent = nullptr; // avoid forwarding events there during destruction |
| 1197 | |
| 1198 | |
| 1199 | { |
| 1200 | const std::lock_guard locker(d->renderJobMutex); |
| 1201 | qDeleteAll(c: std::exchange(obj&: d->beforeSynchronizingJobs, new_val: {})); |
| 1202 | qDeleteAll(c: std::exchange(obj&: d->afterSynchronizingJobs, new_val: {})); |
| 1203 | qDeleteAll(c: std::exchange(obj&: d->beforeRenderingJobs, new_val: {})); |
| 1204 | qDeleteAll(c: std::exchange(obj&: d->afterRenderingJobs, new_val: {}));; |
| 1205 | qDeleteAll(c: std::exchange(obj&: d->afterSwapJobs, new_val: {})); |
| 1206 | } |
| 1207 | |
| 1208 | // It is important that the pixmap cache is cleaned up during shutdown. |
| 1209 | // Besides playing nice, this also solves a practical problem that |
| 1210 | // QQuickTextureFactory implementations in other libraries need |
| 1211 | // have their destructors loaded while they the library is still |
| 1212 | // loaded into memory. |
| 1213 | QQuickPixmap::purgeCache(); |
| 1214 | } |
| 1215 | |
| 1216 | #if QT_CONFIG(quick_shadereffect) |
| 1217 | void qtquick_shadereffect_purge_gui_thread_shader_cache(); |
| 1218 | #endif |
| 1219 | |
| 1220 | /*! |
| 1221 | This function tries to release redundant resources currently held by the QML scene. |
| 1222 | |
| 1223 | Calling this function requests the scene graph to release cached graphics |
| 1224 | resources, such as graphics pipeline objects, shader programs, or image |
| 1225 | data. |
| 1226 | |
| 1227 | Additionally, depending on the render loop in use, this function may also |
| 1228 | result in the scene graph and all window-related rendering resources to be |
| 1229 | released. If this happens, the sceneGraphInvalidated() signal will be |
| 1230 | emitted, allowing users to clean up their own graphics resources. The |
| 1231 | setPersistentGraphics() and setPersistentSceneGraph() functions can be used |
| 1232 | to prevent this from happening, if handling the cleanup is not feasible in |
| 1233 | the application, at the cost of higher memory usage. |
| 1234 | |
| 1235 | \note The releasing of cached graphics resources, such as graphics |
| 1236 | pipelines or shader programs is not dependent on the persistency hints. The |
| 1237 | releasing of those will happen regardless of the values of the persistent |
| 1238 | graphics and scenegraph hints. |
| 1239 | |
| 1240 | \note This function is not related to the QQuickItem::releaseResources() |
| 1241 | virtual function. |
| 1242 | |
| 1243 | \sa sceneGraphInvalidated(), setPersistentGraphics(), setPersistentSceneGraph() |
| 1244 | */ |
| 1245 | |
| 1246 | void QQuickWindow::releaseResources() |
| 1247 | { |
| 1248 | Q_D(QQuickWindow); |
| 1249 | if (d->windowManager) |
| 1250 | d->windowManager->releaseResources(window: this); |
| 1251 | QQuickPixmap::purgeCache(); |
| 1252 | #if QT_CONFIG(quick_shadereffect) |
| 1253 | qtquick_shadereffect_purge_gui_thread_shader_cache(); |
| 1254 | #endif |
| 1255 | } |
| 1256 | |
| 1257 | |
| 1258 | |
| 1259 | /*! |
| 1260 | Sets whether the graphics resources (graphics device or context, |
| 1261 | swapchain, buffers, textures) should be preserved, and cannot be |
| 1262 | released until the last window is deleted, to \a persistent. The |
| 1263 | default value is true. |
| 1264 | |
| 1265 | When calling releaseResources(), or when the window gets hidden (more |
| 1266 | specifically, not renderable), some render loops have the possibility |
| 1267 | to release all, not just the cached, graphics resources. This can free |
| 1268 | up memory temporarily, but it also means the rendering engine will have |
| 1269 | to do a full, potentially costly reinitialization of the resources when |
| 1270 | the window needs to render again. |
| 1271 | |
| 1272 | \note The rules for when a window is not renderable are platform and |
| 1273 | window manager specific. |
| 1274 | |
| 1275 | \note All graphics resources are released when the last QQuickWindow is |
| 1276 | deleted, regardless of this setting. |
| 1277 | |
| 1278 | \note This is a hint, and is not guaranteed that it is taken into account. |
| 1279 | |
| 1280 | \note This hint does not apply to cached resources, that are relatively |
| 1281 | cheap to drop and then recreate later. Therefore, calling releaseResources() |
| 1282 | will typically lead to releasing those regardless of the value of this hint. |
| 1283 | |
| 1284 | \sa setPersistentSceneGraph(), sceneGraphInitialized(), sceneGraphInvalidated(), releaseResources() |
| 1285 | */ |
| 1286 | |
| 1287 | void QQuickWindow::setPersistentGraphics(bool persistent) |
| 1288 | { |
| 1289 | Q_D(QQuickWindow); |
| 1290 | d->persistentGraphics = persistent; |
| 1291 | } |
| 1292 | |
| 1293 | |
| 1294 | |
| 1295 | /*! |
| 1296 | Returns whether essential graphics resources can be released during the |
| 1297 | lifetime of the QQuickWindow. |
| 1298 | |
| 1299 | \note This is a hint, and is not guaranteed that it is taken into account. |
| 1300 | |
| 1301 | \sa setPersistentGraphics() |
| 1302 | */ |
| 1303 | |
| 1304 | bool QQuickWindow::isPersistentGraphics() const |
| 1305 | { |
| 1306 | Q_D(const QQuickWindow); |
| 1307 | return d->persistentGraphics; |
| 1308 | } |
| 1309 | |
| 1310 | |
| 1311 | |
| 1312 | /*! |
| 1313 | Sets whether the scene graph nodes and resources are \a persistent. |
| 1314 | Persistent means the nodes and resources cannot be released. |
| 1315 | The default value is \c true. |
| 1316 | |
| 1317 | When calling releaseResources(), when the window gets hidden (more |
| 1318 | specifically, not renderable), some render loops have the possibility |
| 1319 | to release the scene graph nodes and related graphics resources. This |
| 1320 | frees up memory temporarily, but will also mean the scene graph has to |
| 1321 | be rebuilt when the window renders next time. |
| 1322 | |
| 1323 | \note The rules for when a window is not renderable are platform and |
| 1324 | window manager specific. |
| 1325 | |
| 1326 | \note The scene graph nodes and resources are always released when the |
| 1327 | last QQuickWindow is deleted, regardless of this setting. |
| 1328 | |
| 1329 | \note This is a hint, and is not guaranteed that it is taken into account. |
| 1330 | |
| 1331 | \sa setPersistentGraphics(), sceneGraphInvalidated(), sceneGraphInitialized(), releaseResources() |
| 1332 | */ |
| 1333 | |
| 1334 | void QQuickWindow::setPersistentSceneGraph(bool persistent) |
| 1335 | { |
| 1336 | Q_D(QQuickWindow); |
| 1337 | d->persistentSceneGraph = persistent; |
| 1338 | } |
| 1339 | |
| 1340 | |
| 1341 | |
| 1342 | /*! |
| 1343 | Returns whether the scene graph nodes and resources can be |
| 1344 | released during the lifetime of this QQuickWindow. |
| 1345 | |
| 1346 | \note This is a hint. When and how this happens is implementation |
| 1347 | specific. |
| 1348 | */ |
| 1349 | |
| 1350 | bool QQuickWindow::isPersistentSceneGraph() const |
| 1351 | { |
| 1352 | Q_D(const QQuickWindow); |
| 1353 | return d->persistentSceneGraph; |
| 1354 | } |
| 1355 | |
| 1356 | /*! |
| 1357 | \qmlattachedproperty Item Window::contentItem |
| 1358 | \since 5.4 |
| 1359 | |
| 1360 | This attached property holds the invisible root item of the scene or |
| 1361 | \c null if the item is not in a window. The Window attached property |
| 1362 | can be attached to any Item. |
| 1363 | */ |
| 1364 | |
| 1365 | /*! |
| 1366 | \property QQuickWindow::contentItem |
| 1367 | \brief The invisible root item of the scene. |
| 1368 | |
| 1369 | A QQuickWindow always has a single invisible root item containing all of its content. |
| 1370 | To add items to this window, reparent the items to the contentItem or to an existing |
| 1371 | item in the scene. |
| 1372 | */ |
| 1373 | QQuickItem *QQuickWindow::contentItem() const |
| 1374 | { |
| 1375 | Q_D(const QQuickWindow); |
| 1376 | |
| 1377 | return d->contentItem; |
| 1378 | } |
| 1379 | |
| 1380 | /*! |
| 1381 | \property QQuickWindow::activeFocusItem |
| 1382 | |
| 1383 | \brief The item which currently has active focus or \c null if there is |
| 1384 | no item with active focus. |
| 1385 | |
| 1386 | \sa QQuickItem::forceActiveFocus(), {Keyboard Focus in Qt Quick} |
| 1387 | */ |
| 1388 | QQuickItem *QQuickWindow::activeFocusItem() const |
| 1389 | { |
| 1390 | Q_D(const QQuickWindow); |
| 1391 | auto da = d->deliveryAgentPrivate(); |
| 1392 | Q_ASSERT(da); |
| 1393 | return da->activeFocusItem; |
| 1394 | } |
| 1395 | |
| 1396 | /*! |
| 1397 | \internal |
| 1398 | \reimp |
| 1399 | */ |
| 1400 | QObject *QQuickWindow::focusObject() const |
| 1401 | { |
| 1402 | Q_D(const QQuickWindow); |
| 1403 | auto da = d->deliveryAgentPrivate(); |
| 1404 | Q_ASSERT(da); |
| 1405 | if (!d->inDestructor && da->activeFocusItem) |
| 1406 | return da->activeFocusItem; |
| 1407 | return const_cast<QQuickWindow*>(this); |
| 1408 | } |
| 1409 | |
| 1410 | /*! \reimp */ |
| 1411 | bool QQuickWindow::event(QEvent *event) |
| 1412 | { |
| 1413 | Q_D(QQuickWindow); |
| 1414 | |
| 1415 | // bypass QWindow::event dispatching of input events: deliveryAgent takes care of it |
| 1416 | QQuickDeliveryAgent *da = d->deliveryAgent; |
| 1417 | if (event->isPointerEvent()) { |
| 1418 | /* |
| 1419 | We can't bypass the virtual functions like mousePressEvent() tabletEvent() etc., |
| 1420 | for the sake of code that subclasses QQuickWindow and overrides them, even though |
| 1421 | we no longer need them as entry points for Qt Quick event delivery. |
| 1422 | So dispatch to them now, ahead of normal delivery, and stop them from calling |
| 1423 | back into this function if they were called from here (avoid recursion). |
| 1424 | It could also be that user code expects them to work as entry points, too; |
| 1425 | in that case, windowEventDispatch _won't_ be set, so the event comes here and |
| 1426 | we'll dispatch it further below. |
| 1427 | */ |
| 1428 | if (d->windowEventDispatch) |
| 1429 | return false; |
| 1430 | { |
| 1431 | const bool wasAccepted = event->isAccepted(); |
| 1432 | QBoolBlocker windowEventDispatchGuard(d->windowEventDispatch, true); |
| 1433 | qCDebug(lcPtr) << "dispatching to window functions in case of override" << event; |
| 1434 | QWindow::event(event); |
| 1435 | if (event->isAccepted() && !wasAccepted) |
| 1436 | return true; |
| 1437 | } |
| 1438 | /* |
| 1439 | QQuickWindow does not override touchEvent(). If the application has a subclass |
| 1440 | of QQuickWindow which allows the event to remain accepted, it means they want |
| 1441 | to stop propagation here, so return early (below). But otherwise we will call |
| 1442 | QWindow::touchEvent(), which will ignore(); in that case, we need to continue |
| 1443 | with the usual delivery below, so we need to undo the ignore(). |
| 1444 | */ |
| 1445 | auto pe = static_cast<QPointerEvent *>(event); |
| 1446 | if (QQuickDeliveryAgentPrivate::isTouchEvent(ev: pe)) |
| 1447 | event->accept(); |
| 1448 | // end of dispatch to user-overridden virtual window functions |
| 1449 | |
| 1450 | /* |
| 1451 | When delivering update and release events to existing grabbers, |
| 1452 | use the subscene delivery agent, if any. A possible scenario: |
| 1453 | 1) Two touchpoints pressed on the main window: QQuickWindowPrivate::deliveryAgent delivers to QQuick3DViewport, |
| 1454 | which does picking and finds two subscenes ("root" Items mapped onto two different 3D objects) to deliver it to. |
| 1455 | 2) The QTouchEvent is split up so that each subscene sees points relevant to it. |
| 1456 | 3) During delivery to either subscene, an item in the subscene grabs. |
| 1457 | 4) The user moves finger(s) generating a move event: the correct grabber item needs to get the update |
| 1458 | via the same subscene delivery agent from which it got the press, so that the coord transform will be done properly. |
| 1459 | 5) Likewise with the touchpoint releases. |
| 1460 | With single-point events (mouse, or only one finger) it's simplified: there can only be one subscene of interest; |
| 1461 | for (pt : pe->points()) would only iterate once, so we might as well skip that logic. |
| 1462 | */ |
| 1463 | if (pe->pointCount()) { |
| 1464 | const bool synthMouse = QQuickDeliveryAgentPrivate::isSynthMouse(ev: pe); |
| 1465 | if (QQuickDeliveryAgentPrivate::subsceneAgentsExist) { |
| 1466 | bool ret = false; |
| 1467 | // Split up the multi-point event according to the relevant QQuickDeliveryAgent that should deliver to each existing grabber |
| 1468 | // but send ungrabbed points to d->deliveryAgent() |
| 1469 | QFlatMap<QQuickDeliveryAgent*, QList<QEventPoint>> deliveryAgentsNeedingPoints; |
| 1470 | QEventPoint::States eventStates; |
| 1471 | |
| 1472 | auto insert = [&](QQuickDeliveryAgent *ptda, const QEventPoint &pt) { |
| 1473 | if (pt.state() == QEventPoint::Pressed && !synthMouse) |
| 1474 | pe->clearPassiveGrabbers(point: pt); |
| 1475 | auto &ptList = deliveryAgentsNeedingPoints[ptda]; |
| 1476 | auto idEquals = [](auto id) { return [id] (const auto &e) { return e.id() == id; }; }; |
| 1477 | if (std::none_of(first: ptList.cbegin(), last: ptList.cend(), pred: idEquals(pt.id()))) |
| 1478 | ptList.append(t: pt); |
| 1479 | }; |
| 1480 | |
| 1481 | for (const auto &pt : pe->points()) { |
| 1482 | eventStates |= pt.state(); |
| 1483 | auto epd = QPointingDevicePrivate::get(q: const_cast<QPointingDevice*>(pe->pointingDevice()))->queryPointById(id: pt.id()); |
| 1484 | Q_ASSERT(epd); |
| 1485 | bool foundAgent = false; |
| 1486 | if (!epd->exclusiveGrabber.isNull() && !epd->exclusiveGrabberContext.isNull()) { |
| 1487 | if (auto ptda = qobject_cast<QQuickDeliveryAgent *>(object: epd->exclusiveGrabberContext.data())) { |
| 1488 | insert(ptda, pt); |
| 1489 | qCDebug(lcPtr) << pe->type() << "point" << pt.id() << pt.state() |
| 1490 | << "@" << pt.scenePosition() << "will be re-delivered via known grabbing agent" << ptda << "to" << epd->exclusiveGrabber.data(); |
| 1491 | foundAgent = true; |
| 1492 | } |
| 1493 | } |
| 1494 | for (auto pgda : epd->passiveGrabbersContext) { |
| 1495 | if (auto ptda = qobject_cast<QQuickDeliveryAgent *>(object: pgda.data())) { |
| 1496 | insert(ptda, pt); |
| 1497 | qCDebug(lcPtr) << pe->type() << "point" << pt.id() << pt.state() |
| 1498 | << "@" << pt.scenePosition() << "will be re-delivered via known passive-grabbing agent" << ptda; |
| 1499 | foundAgent = true; |
| 1500 | } |
| 1501 | } |
| 1502 | // fallback: if we didn't find remembered/known grabber agent(s), expect the root DA to handle it |
| 1503 | if (!foundAgent) |
| 1504 | insert(da, pt); |
| 1505 | } |
| 1506 | for (auto daAndPoints : deliveryAgentsNeedingPoints) { |
| 1507 | if (pe->pointCount() > 1) { |
| 1508 | Q_ASSERT(QQuickDeliveryAgentPrivate::isTouchEvent(pe)); |
| 1509 | // if all points have the same state, set the event type accordingly |
| 1510 | QEvent::Type eventType = pe->type(); |
| 1511 | switch (eventStates) { |
| 1512 | case QEventPoint::State::Pressed: |
| 1513 | eventType = QEvent::TouchBegin; |
| 1514 | break; |
| 1515 | case QEventPoint::State::Released: |
| 1516 | eventType = QEvent::TouchEnd; |
| 1517 | break; |
| 1518 | default: |
| 1519 | eventType = QEvent::TouchUpdate; |
| 1520 | break; |
| 1521 | } |
| 1522 | // Make a new touch event for the subscene, the same way QQuickItemPrivate::localizedTouchEvent() does it |
| 1523 | QMutableTouchEvent te(eventType, pe->pointingDevice(), pe->modifiers(), daAndPoints.second); |
| 1524 | te.setTimestamp(pe->timestamp()); |
| 1525 | te.accept(); |
| 1526 | qCDebug(lcTouch) << daAndPoints.first << "shall now receive" << &te; |
| 1527 | ret = daAndPoints.first->event(ev: &te) || ret; |
| 1528 | } else { |
| 1529 | qCDebug(lcPtr) << daAndPoints.first << "shall now receive" << pe; |
| 1530 | ret = daAndPoints.first->event(ev: pe) || ret; |
| 1531 | } |
| 1532 | } |
| 1533 | |
| 1534 | if (ret) { |
| 1535 | d->deliveryAgentPrivate()->clearGrabbers(pointerEvent: pe); |
| 1536 | return true; |
| 1537 | } |
| 1538 | } else if (!synthMouse) { |
| 1539 | // clear passive grabbers unless it's a system synth-mouse event |
| 1540 | // QTBUG-104890: Windows sends synth mouse events (which should be ignored) after touch events |
| 1541 | for (const auto &pt : pe->points()) { |
| 1542 | if (pt.state() == QEventPoint::Pressed) |
| 1543 | pe->clearPassiveGrabbers(point: pt); |
| 1544 | } |
| 1545 | } |
| 1546 | } |
| 1547 | |
| 1548 | // If it has no points, it's probably a TouchCancel, and DeliveryAgent needs to handle it. |
| 1549 | // If we didn't handle it in the block above, handle it now. |
| 1550 | // TODO should we deliver to all DAs at once then, since we don't know which one should get it? |
| 1551 | // or fix QTBUG-90851 so that the event always has points? |
| 1552 | bool ret = (da && da->event(ev: event)); |
| 1553 | |
| 1554 | d->deliveryAgentPrivate()->clearGrabbers(pointerEvent: pe); |
| 1555 | |
| 1556 | if (ret) |
| 1557 | return true; |
| 1558 | } else if (event->isInputEvent()) { |
| 1559 | if (da && da->event(ev: event)) |
| 1560 | return true; |
| 1561 | } |
| 1562 | |
| 1563 | switch (event->type()) { |
| 1564 | // a few more types that are not QInputEvents, but QQuickDeliveryAgent needs to handle them anyway |
| 1565 | case QEvent::FocusAboutToChange: |
| 1566 | case QEvent::Enter: |
| 1567 | case QEvent::Leave: |
| 1568 | case QEvent::InputMethod: |
| 1569 | case QEvent::InputMethodQuery: |
| 1570 | #if QT_CONFIG(quick_draganddrop) |
| 1571 | case QEvent::DragEnter: |
| 1572 | case QEvent::DragLeave: |
| 1573 | case QEvent::DragMove: |
| 1574 | case QEvent::Drop: |
| 1575 | #endif |
| 1576 | if (d->inDestructor) |
| 1577 | return false; |
| 1578 | if (da && da->event(ev: event)) |
| 1579 | return true; |
| 1580 | break; |
| 1581 | case QEvent::LanguageChange: |
| 1582 | case QEvent::LocaleChange: |
| 1583 | if (d->contentItem) |
| 1584 | QCoreApplication::sendEvent(receiver: d->contentItem, event); |
| 1585 | break; |
| 1586 | case QEvent::UpdateRequest: |
| 1587 | if (d->windowManager) |
| 1588 | d->windowManager->handleUpdateRequest(this); |
| 1589 | break; |
| 1590 | case QEvent::PlatformSurface: |
| 1591 | if ((static_cast<QPlatformSurfaceEvent *>(event))->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) { |
| 1592 | // Ensure that the rendering thread is notified before |
| 1593 | // the QPlatformWindow is destroyed. |
| 1594 | if (d->windowManager) |
| 1595 | d->windowManager->hide(window: this); |
| 1596 | } |
| 1597 | break; |
| 1598 | case QEvent::WindowDeactivate: |
| 1599 | if (auto da = d->deliveryAgentPrivate()) |
| 1600 | da->handleWindowDeactivate(win: this); |
| 1601 | Q_FALLTHROUGH(); |
| 1602 | case QEvent::WindowActivate: |
| 1603 | if (d->contentItem) |
| 1604 | QCoreApplication::sendEvent(receiver: d->contentItem, event); |
| 1605 | break; |
| 1606 | case QEvent::ApplicationPaletteChange: |
| 1607 | d->inheritPalette(parentPalette: QGuiApplication::palette()); |
| 1608 | if (d->contentItem) |
| 1609 | QCoreApplication::sendEvent(receiver: d->contentItem, event); |
| 1610 | break; |
| 1611 | case QEvent::DevicePixelRatioChange: |
| 1612 | physicalDpiChanged(); |
| 1613 | break; |
| 1614 | case QEvent::ChildWindowAdded: { |
| 1615 | auto *childEvent = static_cast<QChildWindowEvent*>(event); |
| 1616 | auto *childWindow = childEvent->child(); |
| 1617 | qCDebug(lcQuickWindow) << "Child window" << childWindow << "added to" << this; |
| 1618 | if (childWindow->handle()) { |
| 1619 | // The reparenting has already resulted in the native window |
| 1620 | // being added to its parent, on top of all other windows. We need |
| 1621 | // to do a synchronous re-stacking of the windows here, to avoid |
| 1622 | // leaving the window in the wrong position while waiting for the |
| 1623 | // asynchronous callback to QQuickWindow::polishItems(). |
| 1624 | d->updateChildWindowStackingOrder(); |
| 1625 | } else { |
| 1626 | qCDebug(lcQuickWindow) << "No platform window yet." |
| 1627 | << "Deferring child window stacking until surface creation" ; |
| 1628 | } |
| 1629 | break; |
| 1630 | } |
| 1631 | default: |
| 1632 | break; |
| 1633 | } |
| 1634 | |
| 1635 | if (event->type() == QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)) |
| 1636 | update(); |
| 1637 | else if (event->type() == QEvent::Type(QQuickWindowPrivate::TriggerContextCreationFailure)) |
| 1638 | d->windowManager->handleContextCreationFailure(window: this); |
| 1639 | |
| 1640 | if (event->isPointerEvent()) |
| 1641 | return true; |
| 1642 | else |
| 1643 | return QWindow::event(event); |
| 1644 | } |
| 1645 | |
| 1646 | void QQuickWindowPrivate::updateChildWindowStackingOrder(QQuickItem *item) |
| 1647 | { |
| 1648 | Q_Q(QQuickWindow); |
| 1649 | |
| 1650 | if (!item) { |
| 1651 | qCDebug(lcQuickWindow) << "Updating child window stacking order for" << q; |
| 1652 | item = contentItem; |
| 1653 | } |
| 1654 | auto *itemPrivate = QQuickItemPrivate::get(item); |
| 1655 | const auto paintOrderChildItems = itemPrivate->paintOrderChildItems(); |
| 1656 | for (auto *child : paintOrderChildItems) { |
| 1657 | if (auto *windowContainer = qobject_cast<QQuickWindowContainer*>(object: child)) { |
| 1658 | auto *window = windowContainer->containedWindow(); |
| 1659 | if (!window) { |
| 1660 | qCDebug(lcQuickWindow) << windowContainer << "has no contained window yet" ; |
| 1661 | continue; |
| 1662 | } |
| 1663 | if (window->parent() != q) { |
| 1664 | qCDebug(lcQuickWindow) << window << "is not yet child of this window" ; |
| 1665 | continue; |
| 1666 | } |
| 1667 | qCDebug(lcQuickWindow) << "Raising" << window << "owned by" << windowContainer; |
| 1668 | window->raise(); |
| 1669 | } |
| 1670 | |
| 1671 | updateChildWindowStackingOrder(item: child); |
| 1672 | } |
| 1673 | } |
| 1674 | |
| 1675 | /*! \reimp */ |
| 1676 | void QQuickWindow::keyPressEvent(QKeyEvent *e) |
| 1677 | { |
| 1678 | Q_D(QQuickWindow); |
| 1679 | if (d->windowEventDispatch) |
| 1680 | return; |
| 1681 | auto da = d->deliveryAgentPrivate(); |
| 1682 | Q_ASSERT(da); |
| 1683 | da->deliverKeyEvent(e); |
| 1684 | } |
| 1685 | |
| 1686 | /*! \reimp */ |
| 1687 | void QQuickWindow::keyReleaseEvent(QKeyEvent *e) |
| 1688 | { |
| 1689 | Q_D(QQuickWindow); |
| 1690 | if (d->windowEventDispatch) |
| 1691 | return; |
| 1692 | auto da = d->deliveryAgentPrivate(); |
| 1693 | Q_ASSERT(da); |
| 1694 | da->deliverKeyEvent(e); |
| 1695 | } |
| 1696 | |
| 1697 | #if QT_CONFIG(wheelevent) |
| 1698 | /*! \reimp */ |
| 1699 | void QQuickWindow::wheelEvent(QWheelEvent *event) |
| 1700 | { |
| 1701 | Q_D(QQuickWindow); |
| 1702 | if (d->windowEventDispatch) |
| 1703 | return; |
| 1704 | auto da = d->deliveryAgentPrivate(); |
| 1705 | Q_ASSERT(da); |
| 1706 | da->deliverSinglePointEventUntilAccepted(event); |
| 1707 | } |
| 1708 | #endif // wheelevent |
| 1709 | |
| 1710 | #if QT_CONFIG(tabletevent) |
| 1711 | /*! \reimp */ |
| 1712 | void QQuickWindow::tabletEvent(QTabletEvent *event) |
| 1713 | { |
| 1714 | Q_D(QQuickWindow); |
| 1715 | if (d->windowEventDispatch) |
| 1716 | return; |
| 1717 | auto da = d->deliveryAgentPrivate(); |
| 1718 | Q_ASSERT(da); |
| 1719 | da->deliverPointerEvent(event); |
| 1720 | } |
| 1721 | #endif // tabletevent |
| 1722 | |
| 1723 | /*! \reimp */ |
| 1724 | void QQuickWindow::mousePressEvent(QMouseEvent *event) |
| 1725 | { |
| 1726 | Q_D(QQuickWindow); |
| 1727 | if (d->windowEventDispatch) |
| 1728 | return; |
| 1729 | auto da = d->deliveryAgentPrivate(); |
| 1730 | Q_ASSERT(da); |
| 1731 | da->handleMouseEvent(event); |
| 1732 | } |
| 1733 | /*! \reimp */ |
| 1734 | void QQuickWindow::mouseMoveEvent(QMouseEvent *event) |
| 1735 | { |
| 1736 | Q_D(QQuickWindow); |
| 1737 | if (d->windowEventDispatch) |
| 1738 | return; |
| 1739 | auto da = d->deliveryAgentPrivate(); |
| 1740 | Q_ASSERT(da); |
| 1741 | da->handleMouseEvent(event); |
| 1742 | } |
| 1743 | /*! \reimp */ |
| 1744 | void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event) |
| 1745 | { |
| 1746 | Q_D(QQuickWindow); |
| 1747 | if (d->windowEventDispatch) |
| 1748 | return; |
| 1749 | auto da = d->deliveryAgentPrivate(); |
| 1750 | Q_ASSERT(da); |
| 1751 | da->handleMouseEvent(event); |
| 1752 | } |
| 1753 | /*! \reimp */ |
| 1754 | void QQuickWindow::mouseReleaseEvent(QMouseEvent *event) |
| 1755 | { |
| 1756 | Q_D(QQuickWindow); |
| 1757 | if (d->windowEventDispatch) |
| 1758 | return; |
| 1759 | auto da = d->deliveryAgentPrivate(); |
| 1760 | Q_ASSERT(da); |
| 1761 | da->handleMouseEvent(event); |
| 1762 | } |
| 1763 | |
| 1764 | #if QT_CONFIG(cursor) |
| 1765 | void QQuickWindowPrivate::updateCursor(const QPointF &scenePos, QQuickItem *rootItem) |
| 1766 | { |
| 1767 | Q_Q(QQuickWindow); |
| 1768 | if (!rootItem) |
| 1769 | rootItem = contentItem; |
| 1770 | auto cursorItemAndHandler = findCursorItemAndHandler(item: rootItem, scenePos); |
| 1771 | if (cursorItem != cursorItemAndHandler.first || cursorHandler != cursorItemAndHandler.second || |
| 1772 | (cursorItemAndHandler.second && QQuickPointerHandlerPrivate::get(q: cursorItemAndHandler.second)->cursorDirty)) { |
| 1773 | QWindow *renderWindow = QQuickRenderControl::renderWindowFor(win: q); |
| 1774 | QWindow *window = renderWindow ? renderWindow : q; |
| 1775 | cursorItem = cursorItemAndHandler.first; |
| 1776 | cursorHandler = cursorItemAndHandler.second; |
| 1777 | if (cursorHandler) |
| 1778 | QQuickPointerHandlerPrivate::get(q: cursorItemAndHandler.second)->cursorDirty = false; |
| 1779 | if (cursorItem) { |
| 1780 | const auto cursor = QQuickItemPrivate::get(item: cursorItem)->effectiveCursor(handler: cursorHandler); |
| 1781 | qCDebug(lcHoverTrace) << "setting cursor" << cursor << "from" << cursorHandler << "or" << cursorItem; |
| 1782 | window->setCursor(cursor); |
| 1783 | } else { |
| 1784 | qCDebug(lcHoverTrace) << "unsetting cursor" ; |
| 1785 | window->unsetCursor(); |
| 1786 | } |
| 1787 | } |
| 1788 | } |
| 1789 | |
| 1790 | QPair<QQuickItem*, QQuickPointerHandler*> QQuickWindowPrivate::findCursorItemAndHandler(QQuickItem *item, const QPointF &scenePos) const |
| 1791 | { |
| 1792 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
| 1793 | if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { |
| 1794 | QPointF p = item->mapFromScene(point: scenePos); |
| 1795 | if (!item->contains(point: p)) |
| 1796 | return {nullptr, nullptr}; |
| 1797 | } |
| 1798 | |
| 1799 | if (itemPrivate->subtreeCursorEnabled) { |
| 1800 | QList<QQuickItem *> children = itemPrivate->paintOrderChildItems(); |
| 1801 | for (int ii = children.size() - 1; ii >= 0; --ii) { |
| 1802 | QQuickItem *child = children.at(i: ii); |
| 1803 | if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(item: child)->culled) |
| 1804 | continue; |
| 1805 | auto ret = findCursorItemAndHandler(item: child, scenePos); |
| 1806 | if (ret.first) |
| 1807 | return ret; |
| 1808 | } |
| 1809 | if (itemPrivate->hasCursorHandler) { |
| 1810 | if (auto handler = itemPrivate->effectiveCursorHandler()) { |
| 1811 | if (handler->parentContains(scenePosition: scenePos)) |
| 1812 | return {item, handler}; |
| 1813 | } |
| 1814 | } |
| 1815 | if (itemPrivate->hasCursor) { |
| 1816 | QPointF p = item->mapFromScene(point: scenePos); |
| 1817 | if (item->contains(point: p)) |
| 1818 | return {item, nullptr}; |
| 1819 | } |
| 1820 | } |
| 1821 | |
| 1822 | return {nullptr, nullptr}; |
| 1823 | } |
| 1824 | #endif |
| 1825 | |
| 1826 | void QQuickWindowPrivate::clearFocusObject() |
| 1827 | { |
| 1828 | if (auto da = deliveryAgentPrivate()) |
| 1829 | da->clearFocusObject(); |
| 1830 | } |
| 1831 | |
| 1832 | void QQuickWindowPrivate::setFocusToTarget(FocusTarget target, Qt::FocusReason reason) |
| 1833 | { |
| 1834 | if (!contentItem) |
| 1835 | return; |
| 1836 | |
| 1837 | QQuickItem *newFocusItem = nullptr; |
| 1838 | switch (target) { |
| 1839 | case FocusTarget::First: |
| 1840 | case FocusTarget::Last: { |
| 1841 | const bool forward = (target == FocusTarget::First); |
| 1842 | newFocusItem = QQuickItemPrivate::nextPrevItemInTabFocusChain(item: contentItem, forward); |
| 1843 | if (newFocusItem) { |
| 1844 | const auto *itemPriv = QQuickItemPrivate::get(item: newFocusItem); |
| 1845 | if (itemPriv->subFocusItem && itemPriv->flags & QQuickItem::ItemIsFocusScope) |
| 1846 | clearFocusInScope(scope: newFocusItem, item: itemPriv->subFocusItem, reason); |
| 1847 | } |
| 1848 | break; |
| 1849 | } |
| 1850 | case FocusTarget::Next: |
| 1851 | case FocusTarget::Prev: { |
| 1852 | const auto da = deliveryAgentPrivate(); |
| 1853 | Q_ASSERT(da); |
| 1854 | QQuickItem *focusItem = da->focusTargetItem() ? da->focusTargetItem() : contentItem; |
| 1855 | bool forward = (target == FocusTarget::Next); |
| 1856 | newFocusItem = QQuickItemPrivate::nextPrevItemInTabFocusChain(item: focusItem, forward); |
| 1857 | break; |
| 1858 | } |
| 1859 | default: |
| 1860 | break; |
| 1861 | } |
| 1862 | |
| 1863 | if (newFocusItem) |
| 1864 | newFocusItem->forceActiveFocus(reason); |
| 1865 | } |
| 1866 | |
| 1867 | /*! |
| 1868 | \qmlproperty list<QtObject> Window::data |
| 1869 | \qmldefault |
| 1870 | |
| 1871 | The data property allows you to freely mix visual children, resources |
| 1872 | and other Windows in a Window. |
| 1873 | |
| 1874 | If you assign another Window to the data list, the nested window will |
| 1875 | become "transient for" the outer Window. |
| 1876 | |
| 1877 | If you assign an \l Item to the data list, it becomes a child of the |
| 1878 | Window's \l contentItem, so that it appears inside the window. The item's |
| 1879 | parent will be the window's contentItem, which is the root of the Item |
| 1880 | ownership tree within that Window. |
| 1881 | |
| 1882 | If you assign any other object type, it is added as a resource. |
| 1883 | |
| 1884 | It should not generally be necessary to refer to the \c data property, |
| 1885 | as it is the default property for Window and thus all child items are |
| 1886 | automatically assigned to this property. |
| 1887 | |
| 1888 | \sa QWindow::transientParent() |
| 1889 | */ |
| 1890 | |
| 1891 | void QQuickWindowPrivate::data_append(QQmlListProperty<QObject> *property, QObject *o) |
| 1892 | { |
| 1893 | if (!o) |
| 1894 | return; |
| 1895 | QQuickWindow *that = static_cast<QQuickWindow *>(property->object); |
| 1896 | QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: that->contentItem())->data(); |
| 1897 | itemProperty.append(&itemProperty, o); |
| 1898 | } |
| 1899 | |
| 1900 | qsizetype QQuickWindowPrivate::data_count(QQmlListProperty<QObject> *property) |
| 1901 | { |
| 1902 | QQuickWindow *win = static_cast<QQuickWindow*>(property->object); |
| 1903 | if (!win || !win->contentItem() || !QQuickItemPrivate::get(item: win->contentItem())->data().count) |
| 1904 | return 0; |
| 1905 | QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: win->contentItem())->data(); |
| 1906 | return itemProperty.count(&itemProperty); |
| 1907 | } |
| 1908 | |
| 1909 | QObject *QQuickWindowPrivate::data_at(QQmlListProperty<QObject> *property, qsizetype i) |
| 1910 | { |
| 1911 | QQuickWindow *win = static_cast<QQuickWindow*>(property->object); |
| 1912 | QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: win->contentItem())->data(); |
| 1913 | return itemProperty.at(&itemProperty, i); |
| 1914 | } |
| 1915 | |
| 1916 | void QQuickWindowPrivate::data_clear(QQmlListProperty<QObject> *property) |
| 1917 | { |
| 1918 | QQuickWindow *win = static_cast<QQuickWindow*>(property->object); |
| 1919 | QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: win->contentItem())->data(); |
| 1920 | itemProperty.clear(&itemProperty); |
| 1921 | } |
| 1922 | |
| 1923 | void QQuickWindowPrivate::data_removeLast(QQmlListProperty<QObject> *property) |
| 1924 | { |
| 1925 | QQuickWindow *win = static_cast<QQuickWindow*>(property->object); |
| 1926 | QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: win->contentItem())->data(); |
| 1927 | itemProperty.removeLast(&itemProperty); |
| 1928 | } |
| 1929 | |
| 1930 | bool QQuickWindowPrivate::isRenderable() const |
| 1931 | { |
| 1932 | Q_Q(const QQuickWindow); |
| 1933 | return ((q->isExposed() && q->isVisible())) && q->geometry().isValid(); |
| 1934 | } |
| 1935 | |
| 1936 | void QQuickWindowPrivate::rhiCreationFailureMessage(const QString &backendName, |
| 1937 | QString *translatedMessage, |
| 1938 | QString *untranslatedMessage) |
| 1939 | { |
| 1940 | const char msg[] = QT_TRANSLATE_NOOP("QQuickWindow" , |
| 1941 | "Failed to initialize graphics backend for %1." ); |
| 1942 | *translatedMessage = QQuickWindow::tr(s: msg).arg(a: backendName); |
| 1943 | *untranslatedMessage = QString::fromLatin1(ba: msg).arg(a: backendName); |
| 1944 | } |
| 1945 | |
| 1946 | void QQuickWindowPrivate::cleanupNodes() |
| 1947 | { |
| 1948 | qDeleteAll(c: std::exchange(obj&: cleanupNodeList, new_val: {})); |
| 1949 | } |
| 1950 | |
| 1951 | void QQuickWindowPrivate::cleanupNodesOnShutdown(QQuickItem *item) |
| 1952 | { |
| 1953 | QQuickItemPrivate *p = QQuickItemPrivate::get(item); |
| 1954 | if (p->itemNodeInstance) { |
| 1955 | delete p->itemNodeInstance; |
| 1956 | p->itemNodeInstance = nullptr; |
| 1957 | |
| 1958 | if (p->extra.isAllocated()) { |
| 1959 | p->extra->opacityNode = nullptr; |
| 1960 | p->extra->clipNode = nullptr; |
| 1961 | p->extra->rootNode = nullptr; |
| 1962 | } |
| 1963 | |
| 1964 | p->paintNode = nullptr; |
| 1965 | |
| 1966 | p->dirty(QQuickItemPrivate::Window); |
| 1967 | } |
| 1968 | |
| 1969 | // Qt 7: Make invalidateSceneGraph a virtual member of QQuickItem |
| 1970 | if (p->flags & QQuickItem::ItemHasContents) { |
| 1971 | const QMetaObject *mo = item->metaObject(); |
| 1972 | int index = mo->indexOfSlot(slot: "invalidateSceneGraph()" ); |
| 1973 | if (index >= 0) { |
| 1974 | const QMetaMethod &method = mo->method(index); |
| 1975 | // Skip functions named invalidateSceneGraph() in QML items. |
| 1976 | if (strstr(haystack: method.enclosingMetaObject()->className(), needle: "_QML_" ) == nullptr) |
| 1977 | method.invoke(obj: item, c: Qt::DirectConnection); |
| 1978 | } |
| 1979 | } |
| 1980 | |
| 1981 | for (int ii = 0; ii < p->childItems.size(); ++ii) |
| 1982 | cleanupNodesOnShutdown(item: p->childItems.at(i: ii)); |
| 1983 | } |
| 1984 | |
| 1985 | // This must be called from the render thread, with the main thread frozen |
| 1986 | void QQuickWindowPrivate::cleanupNodesOnShutdown() |
| 1987 | { |
| 1988 | Q_Q(QQuickWindow); |
| 1989 | cleanupNodes(); |
| 1990 | cleanupNodesOnShutdown(item: contentItem); |
| 1991 | for (QSet<QQuickItem *>::const_iterator it = parentlessItems.begin(), cend = parentlessItems.end(); it != cend; ++it) |
| 1992 | cleanupNodesOnShutdown(item: *it); |
| 1993 | animationController->windowNodesDestroyed(); |
| 1994 | q->cleanupSceneGraph(); |
| 1995 | } |
| 1996 | |
| 1997 | void QQuickWindowPrivate::updateDirtyNodes() |
| 1998 | { |
| 1999 | qCDebug(lcDirty) << "QQuickWindowPrivate::updateDirtyNodes():" ; |
| 2000 | |
| 2001 | cleanupNodes(); |
| 2002 | |
| 2003 | QQuickItem *updateList = dirtyItemList; |
| 2004 | dirtyItemList = nullptr; |
| 2005 | if (updateList) QQuickItemPrivate::get(item: updateList)->prevDirtyItem = &updateList; |
| 2006 | |
| 2007 | while (updateList) { |
| 2008 | QQuickItem *item = updateList; |
| 2009 | QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); |
| 2010 | itemPriv->removeFromDirtyList(); |
| 2011 | |
| 2012 | qCDebug(lcDirty) << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString()); |
| 2013 | updateDirtyNode(item); |
| 2014 | } |
| 2015 | } |
| 2016 | |
| 2017 | static inline QSGNode *qquickitem_before_paintNode(QQuickItemPrivate *d) |
| 2018 | { |
| 2019 | const QList<QQuickItem *> childItems = d->paintOrderChildItems(); |
| 2020 | QQuickItem *before = nullptr; |
| 2021 | for (int i=0; i<childItems.size(); ++i) { |
| 2022 | QQuickItemPrivate *dd = QQuickItemPrivate::get(item: childItems.at(i)); |
| 2023 | // Perform the same check as the in fetchNextNode below. |
| 2024 | if (dd->z() < 0 && (dd->explicitVisible || (dd->extra.isAllocated() && dd->extra->effectRefCount))) |
| 2025 | before = childItems.at(i); |
| 2026 | else |
| 2027 | break; |
| 2028 | } |
| 2029 | return Q_UNLIKELY(before) ? QQuickItemPrivate::get(item: before)->itemNode() : nullptr; |
| 2030 | } |
| 2031 | |
| 2032 | static QSGNode *fetchNextNode(QQuickItemPrivate *itemPriv, int &ii, bool &returnedPaintNode) |
| 2033 | { |
| 2034 | QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems(); |
| 2035 | |
| 2036 | for (; ii < orderedChildren.size() && orderedChildren.at(i: ii)->z() < 0; ++ii) { |
| 2037 | QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(item: orderedChildren.at(i: ii)); |
| 2038 | if (!childPrivate->explicitVisible && |
| 2039 | (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount)) |
| 2040 | continue; |
| 2041 | |
| 2042 | ii++; |
| 2043 | return childPrivate->itemNode(); |
| 2044 | } |
| 2045 | |
| 2046 | if (itemPriv->paintNode && !returnedPaintNode) { |
| 2047 | returnedPaintNode = true; |
| 2048 | return itemPriv->paintNode; |
| 2049 | } |
| 2050 | |
| 2051 | for (; ii < orderedChildren.size(); ++ii) { |
| 2052 | QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(item: orderedChildren.at(i: ii)); |
| 2053 | if (!childPrivate->explicitVisible && |
| 2054 | (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount)) |
| 2055 | continue; |
| 2056 | |
| 2057 | ii++; |
| 2058 | return childPrivate->itemNode(); |
| 2059 | } |
| 2060 | |
| 2061 | return nullptr; |
| 2062 | } |
| 2063 | |
| 2064 | void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item) |
| 2065 | { |
| 2066 | QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); |
| 2067 | quint32 dirty = itemPriv->dirtyAttributes; |
| 2068 | itemPriv->dirtyAttributes = 0; |
| 2069 | |
| 2070 | if ((dirty & QQuickItemPrivate::TransformUpdateMask) || |
| 2071 | (dirty & QQuickItemPrivate::Size && itemPriv->origin() != QQuickItem::TopLeft && |
| 2072 | (itemPriv->scale() != 1. || itemPriv->rotation() != 0.))) { |
| 2073 | |
| 2074 | QMatrix4x4 matrix; |
| 2075 | |
| 2076 | if (itemPriv->x != 0. || itemPriv->y != 0.) |
| 2077 | matrix.translate(x: itemPriv->x, y: itemPriv->y); |
| 2078 | |
| 2079 | for (int ii = itemPriv->transforms.size() - 1; ii >= 0; --ii) |
| 2080 | itemPriv->transforms.at(i: ii)->applyTo(matrix: &matrix); |
| 2081 | |
| 2082 | if (itemPriv->scale() != 1. || itemPriv->rotation() != 0.) { |
| 2083 | QPointF origin = item->transformOriginPoint(); |
| 2084 | matrix.translate(x: origin.x(), y: origin.y()); |
| 2085 | if (itemPriv->scale() != 1.) |
| 2086 | matrix.scale(x: itemPriv->scale(), y: itemPriv->scale()); |
| 2087 | if (itemPriv->rotation() != 0.) |
| 2088 | matrix.rotate(angle: itemPriv->rotation(), x: 0, y: 0, z: 1); |
| 2089 | matrix.translate(x: -origin.x(), y: -origin.y()); |
| 2090 | } |
| 2091 | |
| 2092 | itemPriv->itemNode()->setMatrix(matrix); |
| 2093 | } |
| 2094 | |
| 2095 | const bool clipEffectivelyChanged = dirty & (QQuickItemPrivate::Clip | QQuickItemPrivate::Window); |
| 2096 | if (clipEffectivelyChanged) { |
| 2097 | QSGNode *parent = itemPriv->opacityNode() ? (QSGNode *)itemPriv->opacityNode() |
| 2098 | : (QSGNode *)itemPriv->itemNode(); |
| 2099 | QSGNode *child = itemPriv->rootNode(); |
| 2100 | |
| 2101 | if (bool initializeClipNode = item->clip() && itemPriv->clipNode() == nullptr; |
| 2102 | initializeClipNode) { |
| 2103 | QQuickDefaultClipNode *clip = new QQuickDefaultClipNode(item->clipRect()); |
| 2104 | itemPriv->extra.value().clipNode = clip; |
| 2105 | clip->update(); |
| 2106 | |
| 2107 | if (!child) { |
| 2108 | parent->reparentChildNodesTo(newParent: clip); |
| 2109 | parent->appendChildNode(node: clip); |
| 2110 | } else { |
| 2111 | parent->removeChildNode(node: child); |
| 2112 | clip->appendChildNode(node: child); |
| 2113 | parent->appendChildNode(node: clip); |
| 2114 | } |
| 2115 | |
| 2116 | } else if (bool updateClipNode = item->clip() && itemPriv->clipNode() != nullptr; |
| 2117 | updateClipNode) { |
| 2118 | QQuickDefaultClipNode *clip = itemPriv->clipNode(); |
| 2119 | clip->setClipRect(item->clipRect()); |
| 2120 | clip->update(); |
| 2121 | } else if (bool removeClipNode = !item->clip() && itemPriv->clipNode() != nullptr; |
| 2122 | removeClipNode) { |
| 2123 | QQuickDefaultClipNode *clip = itemPriv->clipNode(); |
| 2124 | parent->removeChildNode(node: clip); |
| 2125 | if (child) { |
| 2126 | clip->removeChildNode(node: child); |
| 2127 | parent->appendChildNode(node: child); |
| 2128 | } else { |
| 2129 | clip->reparentChildNodesTo(newParent: parent); |
| 2130 | } |
| 2131 | |
| 2132 | delete itemPriv->clipNode(); |
| 2133 | itemPriv->extra->clipNode = nullptr; |
| 2134 | } |
| 2135 | } |
| 2136 | |
| 2137 | const int effectRefCount = itemPriv->extra.isAllocated() ? itemPriv->extra->effectRefCount : 0; |
| 2138 | const bool effectRefEffectivelyChanged = |
| 2139 | (dirty & (QQuickItemPrivate::EffectReference | QQuickItemPrivate::Window)) |
| 2140 | && ((effectRefCount == 0) != (itemPriv->rootNode() == nullptr)); |
| 2141 | if (effectRefEffectivelyChanged) { |
| 2142 | if (dirty & QQuickItemPrivate::ChildrenUpdateMask) |
| 2143 | itemPriv->childContainerNode()->removeAllChildNodes(); |
| 2144 | |
| 2145 | QSGNode *parent = itemPriv->clipNode(); |
| 2146 | if (!parent) |
| 2147 | parent = itemPriv->opacityNode(); |
| 2148 | if (!parent) |
| 2149 | parent = itemPriv->itemNode(); |
| 2150 | |
| 2151 | if (itemPriv->extra.isAllocated() && itemPriv->extra->effectRefCount) { |
| 2152 | Q_ASSERT(itemPriv->rootNode() == nullptr); |
| 2153 | QSGRootNode *root = new QSGRootNode(); |
| 2154 | itemPriv->extra->rootNode = root; |
| 2155 | parent->reparentChildNodesTo(newParent: root); |
| 2156 | parent->appendChildNode(node: root); |
| 2157 | } else { |
| 2158 | Q_ASSERT(itemPriv->rootNode() != nullptr); |
| 2159 | QSGRootNode *root = itemPriv->rootNode(); |
| 2160 | parent->removeChildNode(node: root); |
| 2161 | root->reparentChildNodesTo(newParent: parent); |
| 2162 | delete itemPriv->rootNode(); |
| 2163 | itemPriv->extra->rootNode = nullptr; |
| 2164 | } |
| 2165 | } |
| 2166 | |
| 2167 | if (dirty & QQuickItemPrivate::ChildrenUpdateMask) { |
| 2168 | int ii = 0; |
| 2169 | bool fetchedPaintNode = false; |
| 2170 | QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems(); |
| 2171 | int desiredNodesSize = orderedChildren.size() + (itemPriv->paintNode ? 1 : 0); |
| 2172 | |
| 2173 | // now start making current state match the promised land of |
| 2174 | // desiredNodes. in the case of our current state matching desiredNodes |
| 2175 | // (though why would we get ChildrenUpdateMask with no changes?) then we |
| 2176 | // should make no changes at all. |
| 2177 | |
| 2178 | // how many nodes did we process, when examining changes |
| 2179 | int desiredNodesProcessed = 0; |
| 2180 | |
| 2181 | // currentNode is how far, in our present tree, we have processed. we |
| 2182 | // make use of this later on to trim the current child list if the |
| 2183 | // desired list is shorter. |
| 2184 | QSGNode *groupNode = itemPriv->childContainerNode(); |
| 2185 | QSGNode *currentNode = groupNode->firstChild(); |
| 2186 | QSGNode *desiredNode = nullptr; |
| 2187 | |
| 2188 | while (currentNode && (desiredNode = fetchNextNode(itemPriv, ii, returnedPaintNode&: fetchedPaintNode))) { |
| 2189 | if (currentNode != desiredNode) { |
| 2190 | // uh oh... reality and our utopic paradise are diverging! |
| 2191 | // we need to reconcile this... |
| 2192 | if (currentNode->nextSibling() == desiredNode) { |
| 2193 | // nice and simple: a node was removed, and the next in line is correct. |
| 2194 | groupNode->removeChildNode(node: currentNode); |
| 2195 | } else { |
| 2196 | // a node needs to be added.. |
| 2197 | // remove it from any pre-existing parent, and push it before currentNode, |
| 2198 | // so it's in the correct place... |
| 2199 | if (desiredNode->parent()) { |
| 2200 | desiredNode->parent()->removeChildNode(node: desiredNode); |
| 2201 | } |
| 2202 | groupNode->insertChildNodeBefore(node: desiredNode, before: currentNode); |
| 2203 | } |
| 2204 | |
| 2205 | // continue iteration at the correct point, now desiredNode is in place... |
| 2206 | currentNode = desiredNode; |
| 2207 | } |
| 2208 | |
| 2209 | currentNode = currentNode->nextSibling(); |
| 2210 | desiredNodesProcessed++; |
| 2211 | } |
| 2212 | |
| 2213 | // if we didn't process as many nodes as in the new list, then we have |
| 2214 | // more nodes at the end of desiredNodes to append to our list. |
| 2215 | // this will be the case when adding new nodes, for instance. |
| 2216 | if (desiredNodesProcessed < desiredNodesSize) { |
| 2217 | while ((desiredNode = fetchNextNode(itemPriv, ii, returnedPaintNode&: fetchedPaintNode))) { |
| 2218 | if (desiredNode->parent()) |
| 2219 | desiredNode->parent()->removeChildNode(node: desiredNode); |
| 2220 | groupNode->appendChildNode(node: desiredNode); |
| 2221 | } |
| 2222 | } else if (currentNode) { |
| 2223 | // on the other hand, if we processed less than our current node |
| 2224 | // tree, then nodes have been _removed_ from the scene, and we need |
| 2225 | // to take care of that here. |
| 2226 | while (currentNode) { |
| 2227 | QSGNode *node = currentNode->nextSibling(); |
| 2228 | groupNode->removeChildNode(node: currentNode); |
| 2229 | currentNode = node; |
| 2230 | } |
| 2231 | } |
| 2232 | } |
| 2233 | |
| 2234 | if ((dirty & QQuickItemPrivate::Size) && itemPriv->clipNode()) { |
| 2235 | itemPriv->clipNode()->setRect(item->clipRect()); |
| 2236 | itemPriv->clipNode()->update(); |
| 2237 | } |
| 2238 | |
| 2239 | if (dirty & (QQuickItemPrivate::OpacityValue | QQuickItemPrivate::Visible |
| 2240 | | QQuickItemPrivate::HideReference | QQuickItemPrivate::Window)) |
| 2241 | { |
| 2242 | qreal opacity = itemPriv->explicitVisible && (!itemPriv->extra.isAllocated() || itemPriv->extra->hideRefCount == 0) |
| 2243 | ? itemPriv->opacity() : qreal(0); |
| 2244 | |
| 2245 | if (opacity != 1 && !itemPriv->opacityNode()) { |
| 2246 | QSGOpacityNode *node = new QSGOpacityNode; |
| 2247 | itemPriv->extra.value().opacityNode = node; |
| 2248 | |
| 2249 | QSGNode *parent = itemPriv->itemNode(); |
| 2250 | QSGNode *child = itemPriv->clipNode(); |
| 2251 | if (!child) |
| 2252 | child = itemPriv->rootNode(); |
| 2253 | |
| 2254 | if (child) { |
| 2255 | parent->removeChildNode(node: child); |
| 2256 | node->appendChildNode(node: child); |
| 2257 | parent->appendChildNode(node); |
| 2258 | } else { |
| 2259 | parent->reparentChildNodesTo(newParent: node); |
| 2260 | parent->appendChildNode(node); |
| 2261 | } |
| 2262 | } |
| 2263 | if (itemPriv->opacityNode()) |
| 2264 | itemPriv->opacityNode()->setOpacity(opacity); |
| 2265 | } |
| 2266 | |
| 2267 | if (dirty & QQuickItemPrivate::ContentUpdateMask) { |
| 2268 | |
| 2269 | if (itemPriv->flags & QQuickItem::ItemHasContents) { |
| 2270 | updatePaintNodeData.transformNode = itemPriv->itemNode(); |
| 2271 | itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData); |
| 2272 | |
| 2273 | Q_ASSERT(itemPriv->paintNode == nullptr || |
| 2274 | itemPriv->paintNode->parent() == nullptr || |
| 2275 | itemPriv->paintNode->parent() == itemPriv->childContainerNode()); |
| 2276 | |
| 2277 | if (itemPriv->paintNode && itemPriv->paintNode->parent() == nullptr) { |
| 2278 | QSGNode *before = qquickitem_before_paintNode(d: itemPriv); |
| 2279 | if (before && before->parent()) { |
| 2280 | Q_ASSERT(before->parent() == itemPriv->childContainerNode()); |
| 2281 | itemPriv->childContainerNode()->insertChildNodeAfter(node: itemPriv->paintNode, after: before); |
| 2282 | } else { |
| 2283 | itemPriv->childContainerNode()->prependChildNode(node: itemPriv->paintNode); |
| 2284 | } |
| 2285 | } |
| 2286 | } else if (itemPriv->paintNode) { |
| 2287 | delete itemPriv->paintNode; |
| 2288 | itemPriv->paintNode = nullptr; |
| 2289 | } |
| 2290 | } |
| 2291 | |
| 2292 | #ifndef QT_NO_DEBUG |
| 2293 | // Check consistency. |
| 2294 | |
| 2295 | QList<QSGNode *> nodes; |
| 2296 | nodes << itemPriv->itemNodeInstance |
| 2297 | << itemPriv->opacityNode() |
| 2298 | << itemPriv->clipNode() |
| 2299 | << itemPriv->rootNode() |
| 2300 | << itemPriv->paintNode; |
| 2301 | nodes.removeAll(t: nullptr); |
| 2302 | |
| 2303 | Q_ASSERT(nodes.constFirst() == itemPriv->itemNodeInstance); |
| 2304 | for (int i=1; i<nodes.size(); ++i) { |
| 2305 | QSGNode *n = nodes.at(i); |
| 2306 | // Failing this means we messed up reparenting |
| 2307 | Q_ASSERT(n->parent() == nodes.at(i-1)); |
| 2308 | // Only the paintNode and the one who is childContainer may have more than one child. |
| 2309 | Q_ASSERT(n == itemPriv->paintNode || n == itemPriv->childContainerNode() || n->childCount() == 1); |
| 2310 | } |
| 2311 | #endif |
| 2312 | |
| 2313 | } |
| 2314 | |
| 2315 | bool QQuickWindowPrivate::emitError(QQuickWindow::SceneGraphError error, const QString &msg) |
| 2316 | { |
| 2317 | Q_Q(QQuickWindow); |
| 2318 | static const QMetaMethod errorSignal = QMetaMethod::fromSignal(signal: &QQuickWindow::sceneGraphError); |
| 2319 | if (q->isSignalConnected(signal: errorSignal)) { |
| 2320 | emit q->sceneGraphError(error, message: msg); |
| 2321 | return true; |
| 2322 | } |
| 2323 | return false; |
| 2324 | } |
| 2325 | |
| 2326 | void QQuickWindow::maybeUpdate() |
| 2327 | { |
| 2328 | Q_D(QQuickWindow); |
| 2329 | if (d->renderControl) |
| 2330 | QQuickRenderControlPrivate::get(renderControl: d->renderControl)->maybeUpdate(); |
| 2331 | else if (d->windowManager) |
| 2332 | d->windowManager->maybeUpdate(window: this); |
| 2333 | } |
| 2334 | |
| 2335 | void QQuickWindow::cleanupSceneGraph() |
| 2336 | { |
| 2337 | Q_D(QQuickWindow); |
| 2338 | if (!d->renderer) |
| 2339 | return; |
| 2340 | |
| 2341 | delete d->renderer->rootNode(); |
| 2342 | delete d->renderer; |
| 2343 | d->renderer = nullptr; |
| 2344 | |
| 2345 | d->runAndClearJobs(jobs: &d->beforeSynchronizingJobs); |
| 2346 | d->runAndClearJobs(jobs: &d->afterSynchronizingJobs); |
| 2347 | d->runAndClearJobs(jobs: &d->beforeRenderingJobs); |
| 2348 | d->runAndClearJobs(jobs: &d->afterRenderingJobs); |
| 2349 | d->runAndClearJobs(jobs: &d->afterSwapJobs); |
| 2350 | } |
| 2351 | |
| 2352 | QOpenGLContext *QQuickWindowPrivate::openglContext() |
| 2353 | { |
| 2354 | #if QT_CONFIG(opengl) |
| 2355 | if (context && context->isValid()) { |
| 2356 | QSGRendererInterface *rif = context->sceneGraphContext()->rendererInterface(renderContext: context); |
| 2357 | if (rif) { |
| 2358 | Q_Q(QQuickWindow); |
| 2359 | return reinterpret_cast<QOpenGLContext *>(rif->getResource(window: q, resource: QSGRendererInterface::OpenGLContextResource)); |
| 2360 | } |
| 2361 | } |
| 2362 | #endif |
| 2363 | return nullptr; |
| 2364 | } |
| 2365 | |
| 2366 | /*! |
| 2367 | Returns true if the scene graph has been initialized; otherwise returns false. |
| 2368 | */ |
| 2369 | bool QQuickWindow::isSceneGraphInitialized() const |
| 2370 | { |
| 2371 | Q_D(const QQuickWindow); |
| 2372 | return d->context != nullptr && d->context->isValid(); |
| 2373 | } |
| 2374 | |
| 2375 | /*! |
| 2376 | \fn void QQuickWindow::frameSwapped() |
| 2377 | |
| 2378 | This signal is emitted when a frame has been queued for presenting. With |
| 2379 | vertical synchronization enabled the signal is emitted at most once per |
| 2380 | vsync interval in a continuously animating scene. |
| 2381 | |
| 2382 | This signal will be emitted from the scene graph rendering thread. |
| 2383 | */ |
| 2384 | |
| 2385 | /*! |
| 2386 | \qmlsignal QtQuick::Window::frameSwapped() |
| 2387 | |
| 2388 | This signal is emitted when a frame has been queued for presenting. With |
| 2389 | vertical synchronization enabled the signal is emitted at most once per |
| 2390 | vsync interval in a continuously animating scene. |
| 2391 | */ |
| 2392 | |
| 2393 | /*! |
| 2394 | \fn void QQuickWindow::sceneGraphInitialized() |
| 2395 | |
| 2396 | This signal is emitted when the scene graph has been initialized. |
| 2397 | |
| 2398 | This signal will be emitted from the scene graph rendering thread. |
| 2399 | */ |
| 2400 | |
| 2401 | /*! |
| 2402 | \qmlsignal QtQuick::Window::sceneGraphInitialized() |
| 2403 | \internal |
| 2404 | */ |
| 2405 | |
| 2406 | /*! |
| 2407 | \fn void QQuickWindow::sceneGraphInvalidated() |
| 2408 | |
| 2409 | This signal is emitted when the scene graph has been invalidated. |
| 2410 | |
| 2411 | This signal implies that the graphics rendering context used |
| 2412 | has been invalidated and all user resources tied to that context |
| 2413 | should be released. |
| 2414 | |
| 2415 | When rendering with OpenGL, the QOpenGLContext of this window will |
| 2416 | be bound when this function is called. The only exception is if |
| 2417 | the native OpenGL has been destroyed outside Qt's control, for |
| 2418 | instance through EGL_CONTEXT_LOST. |
| 2419 | |
| 2420 | This signal will be emitted from the scene graph rendering thread. |
| 2421 | */ |
| 2422 | |
| 2423 | /*! |
| 2424 | \qmlsignal QtQuick::Window::sceneGraphInvalidated() |
| 2425 | \internal |
| 2426 | */ |
| 2427 | |
| 2428 | /*! |
| 2429 | \fn void QQuickWindow::sceneGraphError(SceneGraphError error, const QString &message) |
| 2430 | |
| 2431 | This signal is emitted when an \a error occurred during scene graph initialization. |
| 2432 | |
| 2433 | Applications should connect to this signal if they wish to handle errors, |
| 2434 | like graphics context creation failures, in a custom way. When no slot is |
| 2435 | connected to the signal, the behavior will be different: Quick will print |
| 2436 | the \a message, or show a message box, and terminate the application. |
| 2437 | |
| 2438 | This signal will be emitted from the GUI thread. |
| 2439 | |
| 2440 | \since 5.3 |
| 2441 | */ |
| 2442 | |
| 2443 | /*! |
| 2444 | \qmlsignal QtQuick::Window::sceneGraphError(SceneGraphError error, QString message) |
| 2445 | |
| 2446 | This signal is emitted when an \a error occurred during scene graph initialization. |
| 2447 | |
| 2448 | You can implement onSceneGraphError(error, message) to handle errors, |
| 2449 | such as graphics context creation failures, in a custom way. |
| 2450 | If no handler is connected to this signal, Quick will print the \a message, |
| 2451 | or show a message box, and terminate the application. |
| 2452 | |
| 2453 | \since 5.3 |
| 2454 | */ |
| 2455 | |
| 2456 | /*! |
| 2457 | \class QQuickCloseEvent |
| 2458 | \internal |
| 2459 | \since 5.1 |
| 2460 | |
| 2461 | \inmodule QtQuick |
| 2462 | |
| 2463 | \brief Notification that a \l QQuickWindow is about to be closed |
| 2464 | */ |
| 2465 | /*! |
| 2466 | \qmltype CloseEvent |
| 2467 | \nativetype QQuickCloseEvent |
| 2468 | \inqmlmodule QtQuick |
| 2469 | \ingroup qtquick-visual |
| 2470 | \brief Notification that a \l Window is about to be closed. |
| 2471 | \since 5.1 |
| 2472 | |
| 2473 | Notification that a window is about to be closed by the windowing system |
| 2474 | (e.g. the user clicked the title bar close button). The CloseEvent contains |
| 2475 | an accepted property which can be set to false to abort closing the window. |
| 2476 | */ |
| 2477 | |
| 2478 | /*! |
| 2479 | \qmlproperty bool CloseEvent::accepted |
| 2480 | |
| 2481 | This property indicates whether the application will allow the user to |
| 2482 | close the window. It is true by default. |
| 2483 | */ |
| 2484 | |
| 2485 | /*! |
| 2486 | \internal |
| 2487 | \fn void QQuickWindow::closing(QQuickCloseEvent *close) |
| 2488 | \since 5.1 |
| 2489 | |
| 2490 | This signal is emitted when the window receives the event \a close from |
| 2491 | the windowing system. |
| 2492 | |
| 2493 | On \macOs, Qt will create a menu item \c Quit if there is no menu item |
| 2494 | whose text is "quit" or "exit". This menu item calls the \c QCoreApplication::quit |
| 2495 | signal, not the \c QQuickWindow::closing() signal. |
| 2496 | |
| 2497 | \sa {QMenuBar as a Global Menu Bar} |
| 2498 | */ |
| 2499 | |
| 2500 | /*! |
| 2501 | \qmlsignal QtQuick::Window::closing(CloseEvent close) |
| 2502 | \since 5.1 |
| 2503 | |
| 2504 | This signal is emitted when the user tries to close the window. |
| 2505 | |
| 2506 | This signal includes a \a close parameter. The \c {close.accepted} |
| 2507 | property is true by default so that the window is allowed to close; but you |
| 2508 | can implement an \c onClosing handler and set \c {close.accepted = false} if |
| 2509 | you need to do something else before the window can be closed. |
| 2510 | */ |
| 2511 | |
| 2512 | /*! |
| 2513 | Sets the render target for this window to be \a target. |
| 2514 | |
| 2515 | A QQuickRenderTarget serves as an opaque handle for a renderable native |
| 2516 | object, most commonly a 2D texture, and associated metadata, such as the |
| 2517 | size in pixels. |
| 2518 | |
| 2519 | A default constructed QQuickRenderTarget means no redirection. A valid |
| 2520 | \a target, created via one of the static QQuickRenderTarget factory functions, |
| 2521 | on the other hand, enables redirection of the rendering of the Qt Quick |
| 2522 | scene: it will no longer target the color buffers for the surface |
| 2523 | associated with the window, but rather the textures or other graphics |
| 2524 | objects specified in \a target. |
| 2525 | |
| 2526 | For example, assuming the scenegraph is using Vulkan to render, one can |
| 2527 | redirect its output into a \c VkImage. For graphics APIs like Vulkan, the |
| 2528 | image layout must be provided as well. QQuickRenderTarget instances are |
| 2529 | implicitly shared and are copyable and can be passed by value. They do not |
| 2530 | own the associated native objects (such as, the VkImage in the example), |
| 2531 | however. |
| 2532 | |
| 2533 | \badcode |
| 2534 | QQuickRenderTarget rt = QQuickRenderTarget::fromVulkanImage(vulkanImage, VK_IMAGE_LAYOUT_PREINITIALIZED, pixelSize); |
| 2535 | quickWindow->setRenderTarget(rt); |
| 2536 | \endcode |
| 2537 | |
| 2538 | This function is very often used in combination with QQuickRenderControl |
| 2539 | and an invisible QQuickWindow, in order to render Qt Quick content into a |
| 2540 | texture, without creating an on-screen native window for this QQuickWindow. |
| 2541 | |
| 2542 | When the desired target, or associated data, such as the size, changes, |
| 2543 | call this function with a new QQuickRenderTarget. Constructing |
| 2544 | QQuickRenderTarget instances and calling this function is cheap, but be |
| 2545 | aware that setting a new \a target with a different native object or other |
| 2546 | data may lead to potentially expensive initialization steps when the |
| 2547 | scenegraph is about to render the next frame. Therefore change the target |
| 2548 | only when necessary. |
| 2549 | |
| 2550 | \note The window does not take ownership of any native objects referenced |
| 2551 | in \a target. |
| 2552 | |
| 2553 | \note It is the caller's responsibility to ensure the native objects |
| 2554 | referred to in \a target are valid for the scenegraph renderer too. For |
| 2555 | instance, with Vulkan, Metal, and Direct3D this implies that the texture or |
| 2556 | image is created on the same graphics device that is used by the scenegraph |
| 2557 | internally. Therefore, when texture objects created on an already existing |
| 2558 | device or context are involved, this function is often used in combination |
| 2559 | with setGraphicsDevice(). |
| 2560 | |
| 2561 | \note With graphics APIs where relevant, the application must pay attention |
| 2562 | to image layout transitions performed by the scenegraph. For example, once |
| 2563 | a VkImage is associated with the scenegraph by calling this function, its |
| 2564 | layout will transition to \c VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL when |
| 2565 | rendering a frame. |
| 2566 | |
| 2567 | \warning This function can only be called from the thread doing the |
| 2568 | rendering. |
| 2569 | |
| 2570 | \since 6.0 |
| 2571 | |
| 2572 | \sa QQuickRenderControl, setGraphicsDevice(), setGraphicsApi() |
| 2573 | */ |
| 2574 | void QQuickWindow::setRenderTarget(const QQuickRenderTarget &target) |
| 2575 | { |
| 2576 | Q_D(QQuickWindow); |
| 2577 | if (target != d->customRenderTarget) { |
| 2578 | d->customRenderTarget = target; |
| 2579 | d->redirect.renderTargetDirty = true; |
| 2580 | } |
| 2581 | } |
| 2582 | |
| 2583 | /*! |
| 2584 | \return the QQuickRenderTarget passed to setRenderTarget(), or a default |
| 2585 | constructed one otherwise |
| 2586 | |
| 2587 | \since 6.0 |
| 2588 | |
| 2589 | \sa setRenderTarget() |
| 2590 | */ |
| 2591 | QQuickRenderTarget QQuickWindow::renderTarget() const |
| 2592 | { |
| 2593 | Q_D(const QQuickWindow); |
| 2594 | return d->customRenderTarget; |
| 2595 | } |
| 2596 | |
| 2597 | #ifdef Q_OS_WEBOS |
| 2598 | class GrabWindowForProtectedContent : public QRunnable |
| 2599 | { |
| 2600 | public: |
| 2601 | GrabWindowForProtectedContent(QQuickWindow *window, QImage *image, QWaitCondition *condition) |
| 2602 | : m_window(window) |
| 2603 | , m_image(image) |
| 2604 | , m_condition(condition) |
| 2605 | { |
| 2606 | } |
| 2607 | |
| 2608 | bool checkGrabbable() |
| 2609 | { |
| 2610 | if (!m_window) |
| 2611 | return false; |
| 2612 | if (!m_image) |
| 2613 | return false; |
| 2614 | if (!QQuickWindowPrivate::get(m_window)) |
| 2615 | return false; |
| 2616 | |
| 2617 | return true; |
| 2618 | } |
| 2619 | |
| 2620 | void run() override |
| 2621 | { |
| 2622 | if (!checkGrabbable()) |
| 2623 | return; |
| 2624 | |
| 2625 | *m_image = QSGRhiSupport::instance()->grabOffscreenForProtectedContent(m_window); |
| 2626 | if (m_condition) |
| 2627 | m_condition->wakeOne(); |
| 2628 | return; |
| 2629 | } |
| 2630 | |
| 2631 | private: |
| 2632 | QQuickWindow *m_window; |
| 2633 | QImage *m_image; |
| 2634 | QWaitCondition *m_condition; |
| 2635 | |
| 2636 | }; |
| 2637 | #endif |
| 2638 | |
| 2639 | /*! |
| 2640 | Grabs the contents of the window and returns it as an image. |
| 2641 | |
| 2642 | It is possible to call the grabWindow() function when the window is not |
| 2643 | visible. This requires that the window is \l{QWindow::create()} {created} |
| 2644 | and has a valid size and that no other QQuickWindow instances are rendering |
| 2645 | in the same process. |
| 2646 | |
| 2647 | \note When using this window in combination with QQuickRenderControl, the |
| 2648 | result of this function is an empty image, unless the \c software backend |
| 2649 | is in use. This is because when redirecting the output to an |
| 2650 | application-managed graphics resource (such as, a texture) by using |
| 2651 | QQuickRenderControl and setRenderTarget(), the application is better suited |
| 2652 | for managing and executing an eventual read back operation, since it is in |
| 2653 | full control of the resource to begin with. |
| 2654 | |
| 2655 | \warning Calling this function will cause performance problems. |
| 2656 | |
| 2657 | \warning This function can only be called from the GUI thread. |
| 2658 | */ |
| 2659 | QImage QQuickWindow::grabWindow() |
| 2660 | { |
| 2661 | Q_D(QQuickWindow); |
| 2662 | |
| 2663 | if (!d->isRenderable() && !d->renderControl) { |
| 2664 | // backends like software can grab regardless of the window state |
| 2665 | if (d->windowManager && (d->windowManager->flags() & QSGRenderLoop::SupportsGrabWithoutExpose)) |
| 2666 | return d->windowManager->grab(window: this); |
| 2667 | |
| 2668 | if (!isSceneGraphInitialized()) { |
| 2669 | // We do not have rendering up and running. Forget the render loop, |
| 2670 | // do a frame completely offscreen and synchronously into a |
| 2671 | // texture. This can be *very* slow due to all the device/context |
| 2672 | // and resource initialization but the documentation warns for it, |
| 2673 | // and is still important for some use cases. |
| 2674 | Q_ASSERT(!d->rhi); |
| 2675 | return QSGRhiSupport::instance()->grabOffscreen(window: this); |
| 2676 | } |
| 2677 | } |
| 2678 | |
| 2679 | #ifdef Q_OS_WEBOS |
| 2680 | if (requestedFormat().testOption(QSurfaceFormat::ProtectedContent)) { |
| 2681 | QImage image; |
| 2682 | QMutex mutex; |
| 2683 | QWaitCondition condition; |
| 2684 | mutex.lock(); |
| 2685 | GrabWindowForProtectedContent *job = new GrabWindowForProtectedContent(this, &image, &condition); |
| 2686 | if (!job) { |
| 2687 | qWarning("QQuickWindow::grabWindow: Failed to create a job for capturing protected content" ); |
| 2688 | mutex.unlock(); |
| 2689 | return QImage(); |
| 2690 | } |
| 2691 | scheduleRenderJob(job, QQuickWindow::NoStage); |
| 2692 | condition.wait(&mutex); |
| 2693 | mutex.unlock(); |
| 2694 | return image; |
| 2695 | } |
| 2696 | #endif |
| 2697 | // The common case: we have an exposed window with an initialized |
| 2698 | // scenegraph, meaning we can request grabbing via the render loop, or we |
| 2699 | // are not targeting the window, in which case the request is to be |
| 2700 | // forwarded to the rendercontrol. |
| 2701 | if (d->renderControl) |
| 2702 | return QQuickRenderControlPrivate::get(renderControl: d->renderControl)->grab(); |
| 2703 | else if (d->windowManager) |
| 2704 | return d->windowManager->grab(window: this); |
| 2705 | |
| 2706 | return QImage(); |
| 2707 | } |
| 2708 | |
| 2709 | /*! |
| 2710 | Returns an incubation controller that splices incubation between frames |
| 2711 | for this window. QQuickView automatically installs this controller for you, |
| 2712 | otherwise you will need to install it yourself using \l{QQmlEngine::setIncubationController()}. |
| 2713 | |
| 2714 | The controller is owned by the window and will be destroyed when the window |
| 2715 | is deleted. |
| 2716 | */ |
| 2717 | QQmlIncubationController *QQuickWindow::incubationController() const |
| 2718 | { |
| 2719 | Q_D(const QQuickWindow); |
| 2720 | |
| 2721 | if (!d->windowManager) |
| 2722 | return nullptr; // TODO: make sure that this is safe |
| 2723 | |
| 2724 | if (!d->incubationController) |
| 2725 | d->incubationController = new QQuickWindowIncubationController(d->windowManager); |
| 2726 | return d->incubationController; |
| 2727 | } |
| 2728 | |
| 2729 | |
| 2730 | |
| 2731 | /*! |
| 2732 | \enum QQuickWindow::CreateTextureOption |
| 2733 | |
| 2734 | The CreateTextureOption enums are used to customize a texture is wrapped. |
| 2735 | |
| 2736 | \value TextureHasAlphaChannel The texture has an alpha channel and should |
| 2737 | be drawn using blending. |
| 2738 | |
| 2739 | \value TextureHasMipmaps The texture has mipmaps and can be drawn with |
| 2740 | mipmapping enabled. |
| 2741 | |
| 2742 | \value TextureOwnsGLTexture As of Qt 6.0, this flag is not used in practice |
| 2743 | and is ignored. Native graphics resource ownership is not transferable to |
| 2744 | the wrapping QSGTexture, because Qt Quick may not have the necessary details |
| 2745 | on how such an object and the associated memory should be freed. |
| 2746 | |
| 2747 | \value TextureCanUseAtlas The image can be uploaded into a texture atlas. |
| 2748 | |
| 2749 | \value TextureIsOpaque The texture will return false for |
| 2750 | QSGTexture::hasAlphaChannel() and will not be blended. This flag was added |
| 2751 | in Qt 5.6. |
| 2752 | |
| 2753 | */ |
| 2754 | |
| 2755 | /*! |
| 2756 | \enum QQuickWindow::SceneGraphError |
| 2757 | |
| 2758 | This enum describes the error in a sceneGraphError() signal. |
| 2759 | |
| 2760 | \value ContextNotAvailable graphics context creation failed. This typically means that |
| 2761 | no suitable OpenGL implementation was found, for example because no graphics drivers |
| 2762 | are installed and so no OpenGL 2 support is present. On mobile and embedded boards |
| 2763 | that use OpenGL ES such an error is likely to indicate issues in the windowing system |
| 2764 | integration and possibly an incorrect configuration of Qt. |
| 2765 | |
| 2766 | \since 5.3 |
| 2767 | */ |
| 2768 | |
| 2769 | /*! |
| 2770 | \enum QQuickWindow::TextRenderType |
| 2771 | \since 5.10 |
| 2772 | |
| 2773 | This enum describes the default render type of text-like elements in Qt |
| 2774 | Quick (\l Text, \l TextInput, etc.). |
| 2775 | |
| 2776 | Select NativeTextRendering if you prefer text to look native on the target |
| 2777 | platform and do not require advanced features such as transformation of the |
| 2778 | text. Using such features in combination with the NativeTextRendering |
| 2779 | render type will lend poor and sometimes pixelated results. |
| 2780 | |
| 2781 | Both \c QtTextRendering and \c CurveTextRendering are hardware-accelerated techniques. |
| 2782 | \c QtTextRendering is the faster of the two, but uses more memory and will exhibit rendering |
| 2783 | artifacts at large sizes. \c CurveTextRendering should be considered as an alternative in cases |
| 2784 | where \c QtTextRendering does not give good visual results or where reducing graphics memory |
| 2785 | consumption is a priority. |
| 2786 | |
| 2787 | \value QtTextRendering Use Qt's own rasterization algorithm. |
| 2788 | \value NativeTextRendering Use the operating system's native rasterizer for text. |
| 2789 | \value CurveTextRendering Text is rendered using a curve rasterizer running directly on |
| 2790 | the graphics hardware. (Introduced in Qt 6.7.0.) |
| 2791 | */ |
| 2792 | |
| 2793 | /*! |
| 2794 | \fn void QQuickWindow::beforeSynchronizing() |
| 2795 | |
| 2796 | This signal is emitted before the scene graph is synchronized with the QML state. |
| 2797 | |
| 2798 | Even though the signal is emitted from the scene graph rendering thread, |
| 2799 | the GUI thread is guaranteed to be blocked, like it is in |
| 2800 | QQuickItem::updatePaintNode(). Therefore, it is safe to access GUI thread |
| 2801 | thread data in a slot or lambda that is connected with |
| 2802 | Qt::DirectConnection. |
| 2803 | |
| 2804 | This signal can be used to do any preparation required before calls to |
| 2805 | QQuickItem::updatePaintNode(). |
| 2806 | |
| 2807 | When using OpenGL, the QOpenGLContext used for rendering by the scene graph |
| 2808 | will be bound at this point. |
| 2809 | |
| 2810 | \warning This signal is emitted from the scene graph rendering thread. If your |
| 2811 | slot function needs to finish before execution continues, you must make sure that |
| 2812 | the connection is direct (see Qt::ConnectionType). |
| 2813 | |
| 2814 | \warning When using OpenGL, be aware that setting OpenGL 3.x or 4.x specific |
| 2815 | states and leaving these enabled or set to non-default values when returning |
| 2816 | from the connected slot can interfere with the scene graph's rendering. |
| 2817 | */ |
| 2818 | |
| 2819 | /*! |
| 2820 | \qmlsignal QtQuick::Window::beforeSynchronizing() |
| 2821 | \internal |
| 2822 | */ |
| 2823 | |
| 2824 | /*! |
| 2825 | \fn void QQuickWindow::afterSynchronizing() |
| 2826 | |
| 2827 | This signal is emitted after the scene graph is synchronized with the QML state. |
| 2828 | |
| 2829 | This signal can be used to do preparation required after calls to |
| 2830 | QQuickItem::updatePaintNode(), while the GUI thread is still locked. |
| 2831 | |
| 2832 | When using OpenGL, the QOpenGLContext used for rendering by the scene graph |
| 2833 | will be bound at this point. |
| 2834 | |
| 2835 | \warning This signal is emitted from the scene graph rendering thread. If your |
| 2836 | slot function needs to finish before execution continues, you must make sure that |
| 2837 | the connection is direct (see Qt::ConnectionType). |
| 2838 | |
| 2839 | \warning When using OpenGL, be aware that setting OpenGL 3.x or 4.x specific |
| 2840 | states and leaving these enabled or set to non-default values when returning |
| 2841 | from the connected slot can interfere with the scene graph's rendering. |
| 2842 | |
| 2843 | \since 5.3 |
| 2844 | */ |
| 2845 | |
| 2846 | /*! |
| 2847 | \qmlsignal QtQuick::Window::afterSynchronizing() |
| 2848 | \internal |
| 2849 | \since 5.3 |
| 2850 | */ |
| 2851 | |
| 2852 | /*! |
| 2853 | \fn void QQuickWindow::beforeRendering() |
| 2854 | |
| 2855 | This signal is emitted after the preparations for the frame have been done, |
| 2856 | meaning there is a command buffer in recording mode, where applicable. If |
| 2857 | desired, the slot function connected to this signal can query native |
| 2858 | resources like the command before via QSGRendererInterface. Note however |
| 2859 | that the recording of the main render pass is not yet started at this point |
| 2860 | and it is not possible to add commands within that pass. Starting a pass |
| 2861 | means clearing the color, depth, and stencil buffers so it is not possible |
| 2862 | to achieve an underlay type of rendering by just connecting to this |
| 2863 | signal. Rather, connect to beforeRenderPassRecording(). However, connecting |
| 2864 | to this signal is still important if the recording of copy type of commands |
| 2865 | is desired since those cannot be enqueued within a render pass. |
| 2866 | |
| 2867 | \warning This signal is emitted from the scene graph rendering thread. If your |
| 2868 | slot function needs to finish before execution continues, you must make sure that |
| 2869 | the connection is direct (see Qt::ConnectionType). |
| 2870 | |
| 2871 | \note When using OpenGL, be aware that setting OpenGL 3.x or 4.x specific |
| 2872 | states and leaving these enabled or set to non-default values when |
| 2873 | returning from the connected slot can interfere with the scene graph's |
| 2874 | rendering. The QOpenGLContext used for rendering by the scene graph will be |
| 2875 | bound when the signal is emitted. |
| 2876 | |
| 2877 | \sa rendererInterface(), {Scene Graph - RHI Under QML}, {Scene Graph - |
| 2878 | OpenGL Under QML}, {Scene Graph - Metal Under QML}, {Scene Graph - Vulkan |
| 2879 | Under QML}, {Scene Graph - Direct3D 11 Under QML} |
| 2880 | */ |
| 2881 | |
| 2882 | /*! |
| 2883 | \qmlsignal QtQuick::Window::beforeRendering() |
| 2884 | \internal |
| 2885 | */ |
| 2886 | |
| 2887 | /*! |
| 2888 | \fn void QQuickWindow::afterRendering() |
| 2889 | |
| 2890 | The signal is emitted after scene graph has added its commands to the |
| 2891 | command buffer, which is not yet submitted to the graphics queue. If |
| 2892 | desired, the slot function connected to this signal can query native |
| 2893 | resources, like the command buffer, before via QSGRendererInterface. Note |
| 2894 | however that the render pass (or passes) are already recorded at this point |
| 2895 | and it is not possible to add more commands within the scenegraph's |
| 2896 | pass. Instead, use afterRenderPassRecording() for that. This signal has |
| 2897 | therefore limited use in Qt 6, unlike in Qt 5. Rather, it is the combination |
| 2898 | of beforeRendering() and beforeRenderPassRecording(), or beforeRendering() |
| 2899 | and afterRenderPassRecording(), that is typically used to achieve under- or |
| 2900 | overlaying of the custom rendering. |
| 2901 | |
| 2902 | \warning This signal is emitted from the scene graph rendering thread. If your |
| 2903 | slot function needs to finish before execution continues, you must make sure that |
| 2904 | the connection is direct (see Qt::ConnectionType). |
| 2905 | |
| 2906 | \note When using OpenGL, be aware that setting OpenGL 3.x or 4.x specific |
| 2907 | states and leaving these enabled or set to non-default values when |
| 2908 | returning from the connected slot can interfere with the scene graph's |
| 2909 | rendering. The QOpenGLContext used for rendering by the scene graph will be |
| 2910 | bound when the signal is emitted. |
| 2911 | |
| 2912 | \sa rendererInterface(), {Scene Graph - RHI Under QML}, {Scene Graph - |
| 2913 | OpenGL Under QML}, {Scene Graph - Metal Under QML}, {Scene Graph - Vulkan |
| 2914 | Under QML}, {Scene Graph - Direct3D 11 Under QML} |
| 2915 | */ |
| 2916 | |
| 2917 | /*! |
| 2918 | \qmlsignal QtQuick::Window::afterRendering() |
| 2919 | \internal |
| 2920 | */ |
| 2921 | |
| 2922 | /*! |
| 2923 | \fn void QQuickWindow::beforeRenderPassRecording() |
| 2924 | |
| 2925 | This signal is emitted before the scenegraph starts recording commands for |
| 2926 | the main render pass. (Layers have their own passes and are fully recorded |
| 2927 | by the time this signal is emitted.) The render pass is already active on |
| 2928 | the command buffer when the signal is emitted. |
| 2929 | |
| 2930 | This signal is emitted later than beforeRendering() and it guarantees that |
| 2931 | not just the frame, but also the recording of the scenegraph's main render |
| 2932 | pass is active. This allows inserting commands without having to generate an |
| 2933 | entire, separate render pass (which would typically clear the attached |
| 2934 | images). The native graphics objects can be queried via |
| 2935 | QSGRendererInterface. |
| 2936 | |
| 2937 | \note Resource updates (uploads, copies) typically cannot be enqueued from |
| 2938 | within a render pass. Therefore, more complex user rendering will need to |
| 2939 | connect to both beforeRendering() and this signal. |
| 2940 | |
| 2941 | \warning This signal is emitted from the scene graph rendering thread. If your |
| 2942 | slot function needs to finish before execution continues, you must make sure that |
| 2943 | the connection is direct (see Qt::ConnectionType). |
| 2944 | |
| 2945 | \sa rendererInterface() |
| 2946 | |
| 2947 | \since 5.14 |
| 2948 | |
| 2949 | \sa {Scene Graph - RHI Under QML} |
| 2950 | */ |
| 2951 | |
| 2952 | /*! |
| 2953 | \qmlsignal QtQuick::Window::beforeRenderPassRecording() |
| 2954 | \internal |
| 2955 | \since 5.14 |
| 2956 | */ |
| 2957 | |
| 2958 | /*! |
| 2959 | \fn void QQuickWindow::afterRenderPassRecording() |
| 2960 | |
| 2961 | This signal is emitted after the scenegraph has recorded the commands for |
| 2962 | its main render pass, but the pass is not yet finalized on the command |
| 2963 | buffer. |
| 2964 | |
| 2965 | This signal is emitted earlier than afterRendering(), and it guarantees that |
| 2966 | not just the frame but also the recording of the scenegraph's main render |
| 2967 | pass is still active. This allows inserting commands without having to |
| 2968 | generate an entire, separate render pass (which would typically clear the |
| 2969 | attached images). The native graphics objects can be queried via |
| 2970 | QSGRendererInterface. |
| 2971 | |
| 2972 | \note Resource updates (uploads, copies) typically cannot be enqueued from |
| 2973 | within a render pass. Therefore, more complex user rendering will need to |
| 2974 | connect to both beforeRendering() and this signal. |
| 2975 | |
| 2976 | \warning This signal is emitted from the scene graph rendering thread. If your |
| 2977 | slot function needs to finish before execution continues, you must make sure that |
| 2978 | the connection is direct (see Qt::ConnectionType). |
| 2979 | |
| 2980 | \sa rendererInterface() |
| 2981 | |
| 2982 | \since 5.14 |
| 2983 | |
| 2984 | \sa {Scene Graph - RHI Under QML} |
| 2985 | */ |
| 2986 | |
| 2987 | /*! |
| 2988 | \fn void QQuickWindow::beforeFrameBegin() |
| 2989 | |
| 2990 | This signal is emitted before the scene graph starts preparing the frame. |
| 2991 | This precedes signals like beforeSynchronizing() or beforeRendering(). It is |
| 2992 | the earliest signal that is emitted by the scene graph rendering thread |
| 2993 | when starting to prepare a new frame. |
| 2994 | |
| 2995 | This signal is relevant for lower level graphics frameworks that need to |
| 2996 | execute certain operations, such as resource cleanup, at a stage where Qt |
| 2997 | Quick has not initiated the recording of a new frame via the underlying |
| 2998 | rendering hardware interface APIs. |
| 2999 | |
| 3000 | \warning This signal is emitted from the scene graph rendering thread. If your |
| 3001 | slot function needs to finish before execution continues, you must make sure that |
| 3002 | the connection is direct (see Qt::ConnectionType). |
| 3003 | |
| 3004 | \since 6.0 |
| 3005 | |
| 3006 | \sa afterFrameEnd(), rendererInterface() |
| 3007 | */ |
| 3008 | |
| 3009 | /*! |
| 3010 | \qmlsignal QtQuick::Window::beforeFrameBegin() |
| 3011 | \internal |
| 3012 | */ |
| 3013 | |
| 3014 | /*! |
| 3015 | \fn void QQuickWindow::afterFrameEnd() |
| 3016 | |
| 3017 | This signal is emitted when the scene graph has submitted a frame. This is |
| 3018 | emitted after all other related signals, such as afterRendering(). It is |
| 3019 | the last signal that is emitted by the scene graph rendering thread when |
| 3020 | rendering a frame. |
| 3021 | |
| 3022 | \note Unlike frameSwapped(), this signal is guaranteed to be emitted also |
| 3023 | when the Qt Quick output is redirected via QQuickRenderControl. |
| 3024 | |
| 3025 | \warning This signal is emitted from the scene graph rendering thread. If your |
| 3026 | slot function needs to finish before execution continues, you must make sure that |
| 3027 | the connection is direct (see Qt::ConnectionType). |
| 3028 | |
| 3029 | \since 6.0 |
| 3030 | |
| 3031 | \sa beforeFrameBegin(), rendererInterface() |
| 3032 | */ |
| 3033 | |
| 3034 | /*! |
| 3035 | \qmlsignal QtQuick::Window::afterFrameEnd() |
| 3036 | \internal |
| 3037 | */ |
| 3038 | |
| 3039 | /*! |
| 3040 | \qmlsignal QtQuick::Window::afterRenderPassRecording() |
| 3041 | \internal |
| 3042 | \since 5.14 |
| 3043 | */ |
| 3044 | |
| 3045 | /*! |
| 3046 | \fn void QQuickWindow::afterAnimating() |
| 3047 | |
| 3048 | This signal is emitted on the GUI thread before requesting the render thread to |
| 3049 | perform the synchronization of the scene graph. |
| 3050 | |
| 3051 | Unlike the other similar signals, this one is emitted on the GUI thread |
| 3052 | instead of the render thread. It can be used to synchronize external |
| 3053 | animation systems with the QML content. At the same time this means that |
| 3054 | this signal is not suitable for triggering graphics operations. |
| 3055 | |
| 3056 | \since 5.3 |
| 3057 | */ |
| 3058 | |
| 3059 | /*! |
| 3060 | \qmlsignal QtQuick::Window::afterAnimating() |
| 3061 | |
| 3062 | This signal is emitted on the GUI thread before requesting the render thread to |
| 3063 | perform the synchronization of the scene graph. |
| 3064 | |
| 3065 | You can implement onAfterAnimating to do additional processing after each animation step. |
| 3066 | |
| 3067 | \since 5.3 |
| 3068 | */ |
| 3069 | |
| 3070 | /*! |
| 3071 | \fn void QQuickWindow::sceneGraphAboutToStop() |
| 3072 | |
| 3073 | This signal is emitted on the render thread when the scene graph is |
| 3074 | about to stop rendering. This happens usually because the window |
| 3075 | has been hidden. |
| 3076 | |
| 3077 | Applications may use this signal to release resources, but should be |
| 3078 | prepared to reinstantiated them again fast. The scene graph and the |
| 3079 | graphics context are not released at this time. |
| 3080 | |
| 3081 | \warning This signal is emitted from the scene graph rendering thread. If your |
| 3082 | slot function needs to finish before execution continues, you must make sure that |
| 3083 | the connection is direct (see Qt::ConnectionType). |
| 3084 | |
| 3085 | \warning Make very sure that a signal handler for sceneGraphAboutToStop() leaves the |
| 3086 | graphics context in the same state as it was when the signal handler was entered. |
| 3087 | Failing to do so can result in the scene not rendering properly. |
| 3088 | |
| 3089 | \sa sceneGraphInvalidated() |
| 3090 | \since 5.3 |
| 3091 | */ |
| 3092 | |
| 3093 | /*! |
| 3094 | \qmlsignal QtQuick::Window::sceneGraphAboutToStop() |
| 3095 | \internal |
| 3096 | \since 5.3 |
| 3097 | */ |
| 3098 | |
| 3099 | /*! |
| 3100 | \overload |
| 3101 | */ |
| 3102 | |
| 3103 | QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const |
| 3104 | { |
| 3105 | return createTextureFromImage(image, options: {}); |
| 3106 | } |
| 3107 | |
| 3108 | |
| 3109 | /*! |
| 3110 | Creates a new QSGTexture from the supplied \a image. If the image has an |
| 3111 | alpha channel, the corresponding texture will have an alpha channel. |
| 3112 | |
| 3113 | The caller of the function is responsible for deleting the returned texture. |
| 3114 | The underlying native texture object is then destroyed together with the |
| 3115 | QSGTexture. |
| 3116 | |
| 3117 | When \a options contains TextureCanUseAtlas, the engine may put the image |
| 3118 | into a texture atlas. Textures in an atlas need to rely on |
| 3119 | QSGTexture::normalizedTextureSubRect() for their geometry and will not |
| 3120 | support QSGTexture::Repeat. Other values from CreateTextureOption are |
| 3121 | ignored. |
| 3122 | |
| 3123 | When \a options contains TextureIsOpaque, the engine will create an RGB |
| 3124 | texture which returns false for QSGTexture::hasAlphaChannel(). Opaque |
| 3125 | textures will in most cases be faster to render. When this flag is not set, |
| 3126 | the texture will have an alpha channel based on the image's format. |
| 3127 | |
| 3128 | When \a options contains TextureHasMipmaps, the engine will create a texture |
| 3129 | which can use mipmap filtering. Mipmapped textures can not be in an atlas. |
| 3130 | |
| 3131 | Setting TextureHasAlphaChannel in \a options serves no purpose for this |
| 3132 | function since assuming an alpha channel and blending is the default. To opt |
| 3133 | out, set TextureIsOpaque. |
| 3134 | |
| 3135 | When the scene graph uses OpenGL, the returned texture will be using \c |
| 3136 | GL_TEXTURE_2D as texture target and \c GL_RGBA as internal format. With |
| 3137 | other graphics APIs, the texture format is typically \c RGBA8. Reimplement |
| 3138 | QSGTexture to create textures with different parameters. |
| 3139 | |
| 3140 | \warning This function will return 0 if the scene graph has not yet been |
| 3141 | initialized. |
| 3142 | |
| 3143 | \warning The returned texture is not memory managed by the scene graph and |
| 3144 | must be explicitly deleted by the caller on the rendering thread. This is |
| 3145 | achieved by deleting the texture from a QSGNode destructor or by using |
| 3146 | deleteLater() in the case where the texture already has affinity to the |
| 3147 | rendering thread. |
| 3148 | |
| 3149 | This function can be called from both the main and the render thread. |
| 3150 | |
| 3151 | \sa sceneGraphInitialized(), QSGTexture |
| 3152 | */ |
| 3153 | |
| 3154 | QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateTextureOptions options) const |
| 3155 | { |
| 3156 | Q_D(const QQuickWindow); |
| 3157 | if (!isSceneGraphInitialized()) // check both for d->context and d->context->isValid() |
| 3158 | return nullptr; |
| 3159 | uint flags = 0; |
| 3160 | if (options & TextureCanUseAtlas) flags |= QSGRenderContext::CreateTexture_Atlas; |
| 3161 | if (options & TextureHasMipmaps) flags |= QSGRenderContext::CreateTexture_Mipmap; |
| 3162 | if (!(options & TextureIsOpaque)) flags |= QSGRenderContext::CreateTexture_Alpha; |
| 3163 | return d->context->createTexture(image, flags); |
| 3164 | } |
| 3165 | |
| 3166 | /*! |
| 3167 | Creates a new QSGTexture from the supplied \a texture. |
| 3168 | |
| 3169 | Use \a options to customize the texture attributes. Only the |
| 3170 | TextureHasAlphaChannel flag is taken into account by this function. When |
| 3171 | set, the resulting QSGTexture is always treated by the scene graph renderer |
| 3172 | as needing blending. For textures that are fully opaque, not setting the |
| 3173 | flag can save the cost of performing alpha blending during rendering. The |
| 3174 | flag has no direct correspondence to the \l{QRhiTexture::format()}{format} |
| 3175 | of the QRhiTexture, i.e. not setting the flag while having a texture format |
| 3176 | such as the commonly used \l QRhiTexture::RGBA8 is perfectly normal. |
| 3177 | |
| 3178 | Mipmapping is not controlled by \a options since \a texture is already |
| 3179 | created and has the presence or lack of mipmaps baked in. |
| 3180 | |
| 3181 | The returned QSGTexture owns the QRhiTexture, meaning \a texture is |
| 3182 | destroyed together with the returned QSGTexture. |
| 3183 | |
| 3184 | If \a texture owns its underlying native graphics resources (OpenGL texture |
| 3185 | object, Vulkan image, etc.), that depends on how the QRhiTexture was created |
| 3186 | (\l{QRhiTexture::create()} or \l{QRhiTexture::createFrom()}), and that is |
| 3187 | not controlled or changed by this function. |
| 3188 | |
| 3189 | \note This is only functional when the scene graph has already initialized |
| 3190 | and is using the default, \l{QRhi}-based \l{Scene Graph |
| 3191 | Adaptations}{adaptation}. The return value is \nullptr otherwise. |
| 3192 | |
| 3193 | \note This function can only be called on the scene graph render thread. |
| 3194 | |
| 3195 | \since 6.6 |
| 3196 | |
| 3197 | \sa createTextureFromImage(), sceneGraphInitialized(), QSGTexture |
| 3198 | */ |
| 3199 | QSGTexture *QQuickWindow::createTextureFromRhiTexture(QRhiTexture *texture, CreateTextureOptions options) const |
| 3200 | { |
| 3201 | Q_D(const QQuickWindow); |
| 3202 | if (!d->rhi) |
| 3203 | return nullptr; |
| 3204 | |
| 3205 | QSGPlainTexture *t = new QSGPlainTexture; |
| 3206 | t->setOwnsTexture(true); |
| 3207 | t->setTexture(texture); |
| 3208 | t->setHasAlphaChannel(options & QQuickWindow::TextureHasAlphaChannel); |
| 3209 | t->setTextureSize(texture->pixelSize()); |
| 3210 | return t; |
| 3211 | } |
| 3212 | |
| 3213 | // Legacy, private alternative to createTextureFromRhiTexture() that internally |
| 3214 | // creates a QRhiTexture wrapping the existing native graphics resource. |
| 3215 | // New code should prefer using the public API. |
| 3216 | QSGTexture *QQuickWindowPrivate::createTextureFromNativeTexture(quint64 nativeObjectHandle, |
| 3217 | int nativeLayoutOrState, |
| 3218 | uint nativeFormat, |
| 3219 | const QSize &size, |
| 3220 | QQuickWindow::CreateTextureOptions options, |
| 3221 | TextureFromNativeTextureFlags flags) const |
| 3222 | { |
| 3223 | if (!rhi) |
| 3224 | return nullptr; |
| 3225 | |
| 3226 | QSGPlainTexture *texture = new QSGPlainTexture; |
| 3227 | texture->setTextureFromNativeTexture(rhi, nativeObjectHandle, nativeLayoutOrState, nativeFormat, |
| 3228 | size, options, flags); |
| 3229 | texture->setHasAlphaChannel(options & QQuickWindow::TextureHasAlphaChannel); |
| 3230 | // note that the QRhiTexture does not (and cannot) own the native object |
| 3231 | texture->setOwnsTexture(true); // texture meaning the QRhiTexture here, not the native object |
| 3232 | texture->setTextureSize(size); |
| 3233 | return texture; |
| 3234 | } |
| 3235 | |
| 3236 | /*! |
| 3237 | \qmlproperty color Window::color |
| 3238 | |
| 3239 | The background color for the window. |
| 3240 | |
| 3241 | Setting this property is more efficient than using a separate Rectangle. |
| 3242 | |
| 3243 | \note If you set the color to \c "transparent" or to a color with alpha translucency, |
| 3244 | you should also set suitable \l flags such as \c {flags: Qt.FramelessWindowHint}. |
| 3245 | Otherwise, window translucency may not be enabled consistently on all platforms. |
| 3246 | */ |
| 3247 | |
| 3248 | /*! |
| 3249 | \property QQuickWindow::color |
| 3250 | \brief The color used to clear the color buffer at the beginning of each frame. |
| 3251 | |
| 3252 | By default, the clear color is white. |
| 3253 | |
| 3254 | \sa setDefaultAlphaBuffer() |
| 3255 | */ |
| 3256 | |
| 3257 | void QQuickWindow::setColor(const QColor &color) |
| 3258 | { |
| 3259 | Q_D(QQuickWindow); |
| 3260 | if (color == d->clearColor) |
| 3261 | return; |
| 3262 | |
| 3263 | if (color.alpha() != d->clearColor.alpha()) { |
| 3264 | QSurfaceFormat fmt = requestedFormat(); |
| 3265 | if (color.alpha() < 255) |
| 3266 | fmt.setAlphaBufferSize(8); |
| 3267 | else |
| 3268 | fmt.setAlphaBufferSize(-1); |
| 3269 | setFormat(fmt); |
| 3270 | } |
| 3271 | d->clearColor = color; |
| 3272 | emit colorChanged(color); |
| 3273 | update(); |
| 3274 | } |
| 3275 | |
| 3276 | QColor QQuickWindow::color() const |
| 3277 | { |
| 3278 | return d_func()->clearColor; |
| 3279 | } |
| 3280 | |
| 3281 | /*! |
| 3282 | \brief Returns whether to use alpha transparency on newly created windows. |
| 3283 | |
| 3284 | \since 5.1 |
| 3285 | \sa setDefaultAlphaBuffer() |
| 3286 | */ |
| 3287 | bool QQuickWindow::hasDefaultAlphaBuffer() |
| 3288 | { |
| 3289 | return QQuickWindowPrivate::defaultAlphaBuffer; |
| 3290 | } |
| 3291 | |
| 3292 | /*! |
| 3293 | \brief \a useAlpha specifies whether to use alpha transparency on newly created windows. |
| 3294 | \since 5.1 |
| 3295 | |
| 3296 | In any application which expects to create translucent windows, it's necessary to set |
| 3297 | this to true before creating the first QQuickWindow. The default value is false. |
| 3298 | |
| 3299 | \sa hasDefaultAlphaBuffer() |
| 3300 | */ |
| 3301 | void QQuickWindow::setDefaultAlphaBuffer(bool useAlpha) |
| 3302 | { |
| 3303 | QQuickWindowPrivate::defaultAlphaBuffer = useAlpha; |
| 3304 | } |
| 3305 | |
| 3306 | /*! |
| 3307 | \struct QQuickWindow::GraphicsStateInfo |
| 3308 | \inmodule QtQuick |
| 3309 | \since 5.14 |
| 3310 | |
| 3311 | \brief Describes some of the RHI's graphics state at the point of a |
| 3312 | \l{QQuickWindow::beginExternalCommands()}{beginExternalCommands()} call. |
| 3313 | */ |
| 3314 | |
| 3315 | /*! |
| 3316 | \variable QQuickWindow::GraphicsStateInfo::currentFrameSlot |
| 3317 | \since 5.14 |
| 3318 | \brief the current frame slot index while recording a frame. |
| 3319 | |
| 3320 | When the scenegraph renders with lower level 3D APIs such as Vulkan or |
| 3321 | Metal, it is the Qt's responsibility to ensure blocking whenever starting a |
| 3322 | new frame and finding the CPU is already a certain number of frames ahead |
| 3323 | of the GPU (because the command buffer submitted in frame no. \c{current} - |
| 3324 | \c{FramesInFlight} has not yet completed). With other graphics APIs, such |
| 3325 | as OpenGL or Direct 3D 11 this level of control is not exposed to the API |
| 3326 | client but rather handled by the implementation of the graphics API. |
| 3327 | |
| 3328 | By extension, this also means that the appropriate double (or triple) |
| 3329 | buffering of resources, such as buffers, is up to the graphics API client |
| 3330 | to manage. Most commonly, a uniform buffer where the data changes between |
| 3331 | frames cannot simply change its contents when submitting a frame, given |
| 3332 | that the frame may still be active ("in flight") when starting to record |
| 3333 | the next frame. To avoid stalling the pipeline, one way is to have multiple |
| 3334 | buffers (and memory allocations) under the hood, thus realizing at least a |
| 3335 | double buffered scheme for such resources. |
| 3336 | |
| 3337 | Applications that integrate rendering done directly with a graphics API |
| 3338 | such as Vulkan may want to perform a similar double or triple buffering of |
| 3339 | their own graphics resources, in a way that is compatible with the Qt |
| 3340 | rendering engine's frame submission process. That then involves knowing the |
| 3341 | values for the maximum number of in-flight frames (which is typically 2 or |
| 3342 | 3) and the current frame slot index, which is a number running 0, 1, .., |
| 3343 | FramesInFlight-1, and then wrapping around. The former is exposed in the |
| 3344 | \l{QQuickWindow::GraphicsStateInfo::framesInFlight}{framesInFlight} |
| 3345 | variable. The latter, current index, is this value. |
| 3346 | |
| 3347 | For an example of using these values in practice, refer to the {Scene Graph |
| 3348 | - Vulkan Under QML} and {Scene Graph - Vulkan Texture Import} examples. |
| 3349 | */ |
| 3350 | |
| 3351 | /*! |
| 3352 | \variable QQuickWindow::GraphicsStateInfo::framesInFlight |
| 3353 | \since 5.14 |
| 3354 | \brief the maximum number of frames kept in flight. |
| 3355 | |
| 3356 | See \l{QQuickWindow::GraphicsStateInfo::currentFrameSlot}{currentFrameSlot} |
| 3357 | for a detailed description. |
| 3358 | */ |
| 3359 | |
| 3360 | /*! |
| 3361 | \return a reference to a GraphicsStateInfo struct describing some of the |
| 3362 | RHI's internal state, in particular, the double or tripple buffering status |
| 3363 | of the backend (such as, the Vulkan or Metal integrations). This is |
| 3364 | relevant when the underlying graphics APIs is Vulkan or Metal, and the |
| 3365 | external rendering code wishes to perform double or tripple buffering of |
| 3366 | its own often-changing resources, such as, uniform buffers, in order to |
| 3367 | avoid stalling the pipeline. |
| 3368 | */ |
| 3369 | const QQuickWindow::GraphicsStateInfo &QQuickWindow::graphicsStateInfo() |
| 3370 | { |
| 3371 | Q_D(QQuickWindow); |
| 3372 | if (d->rhi) { |
| 3373 | d->rhiStateInfo.currentFrameSlot = d->rhi->currentFrameSlot(); |
| 3374 | d->rhiStateInfo.framesInFlight = d->rhi->resourceLimit(limit: QRhi::FramesInFlight); |
| 3375 | } |
| 3376 | return d->rhiStateInfo; |
| 3377 | } |
| 3378 | |
| 3379 | /*! |
| 3380 | When mixing raw graphics (OpenGL, Vulkan, Metal, etc.) commands with scene |
| 3381 | graph rendering, it is necessary to call this function before recording |
| 3382 | commands to the command buffer used by the scene graph to render its main |
| 3383 | render pass. This is to avoid clobbering state. |
| 3384 | |
| 3385 | In practice this function is often called from a slot connected to the |
| 3386 | beforeRenderPassRecording() or afterRenderPassRecording() signals. |
| 3387 | |
| 3388 | The function does not need to be called when recording commands to the |
| 3389 | application's own command buffer (such as, a VkCommandBuffer or |
| 3390 | MTLCommandBuffer + MTLRenderCommandEncoder created and managed by the |
| 3391 | application, not retrieved from the scene graph). With graphics APIs where |
| 3392 | no native command buffer concept is exposed (OpenGL, Direct 3D 11), |
| 3393 | beginExternalCommands() and endExternalCommands() together provide a |
| 3394 | replacement for the Qt 5 resetOpenGLState() function. |
| 3395 | |
| 3396 | Calling this function and endExternalCommands() is not necessary within the |
| 3397 | \l{QSGRenderNode::render()}{render()} implementation of a QSGRenderNode |
| 3398 | because the scene graph performs the necessary steps implicitly for render |
| 3399 | nodes. |
| 3400 | |
| 3401 | Native graphics objects (such as, graphics device, command buffer or |
| 3402 | encoder) are accessible via QSGRendererInterface::getResource(). |
| 3403 | |
| 3404 | \warning Watch out for the fact that |
| 3405 | QSGRendererInterface::CommandListResource may return a different object |
| 3406 | between beginExternalCommands() - endExternalCommands(). This can happen |
| 3407 | when the underlying implementation provides a dedicated secondary command |
| 3408 | buffer for recording external graphics commands within a render pass. |
| 3409 | Therefore, always query CommandListResource after calling this function. Do |
| 3410 | not attempt to reuse an object from an earlier query. |
| 3411 | |
| 3412 | \note When the scenegraph is using OpenGL, pay attention to the fact that |
| 3413 | the OpenGL state in the context can have arbitrary settings, and this |
| 3414 | function does not perform any resetting of the state back to defaults. |
| 3415 | |
| 3416 | \sa endExternalCommands(), QQuickOpenGLUtils::resetOpenGLState() |
| 3417 | |
| 3418 | \since 5.14 |
| 3419 | */ |
| 3420 | void QQuickWindow::beginExternalCommands() |
| 3421 | { |
| 3422 | Q_D(QQuickWindow); |
| 3423 | if (d->rhi && d->context && d->context->isValid()) { |
| 3424 | QSGDefaultRenderContext *rc = static_cast<QSGDefaultRenderContext *>(d->context); |
| 3425 | QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer(); |
| 3426 | if (cb) |
| 3427 | cb->beginExternal(); |
| 3428 | } |
| 3429 | } |
| 3430 | |
| 3431 | /*! |
| 3432 | When mixing raw graphics (OpenGL, Vulkan, Metal, etc.) commands with scene |
| 3433 | graph rendering, it is necessary to call this function after recording |
| 3434 | commands to the command buffer used by the scene graph to render its main |
| 3435 | render pass. This is to avoid clobbering state. |
| 3436 | |
| 3437 | In practice this function is often called from a slot connected to the |
| 3438 | beforeRenderPassRecording() or afterRenderPassRecording() signals. |
| 3439 | |
| 3440 | The function does not need to be called when recording commands to the |
| 3441 | application's own command buffer (such as, a VkCommandBuffer or |
| 3442 | MTLCommandBuffer + MTLRenderCommandEncoder created and managed by the |
| 3443 | application, not retrieved from the scene graph). With graphics APIs where |
| 3444 | no native command buffer concept is exposed (OpenGL, Direct 3D 11), |
| 3445 | beginExternalCommands() and endExternalCommands() together provide a |
| 3446 | replacement for the Qt 5 resetOpenGLState() function. |
| 3447 | |
| 3448 | Calling this function and beginExternalCommands() is not necessary within the |
| 3449 | \l{QSGRenderNode::render()}{render()} implementation of a QSGRenderNode |
| 3450 | because the scene graph performs the necessary steps implicitly for render |
| 3451 | nodes. |
| 3452 | |
| 3453 | \sa beginExternalCommands(), QQuickOpenGLUtils::resetOpenGLState() |
| 3454 | |
| 3455 | \since 5.14 |
| 3456 | */ |
| 3457 | void QQuickWindow::endExternalCommands() |
| 3458 | { |
| 3459 | Q_D(QQuickWindow); |
| 3460 | if (d->rhi && d->context && d->context->isValid()) { |
| 3461 | QSGDefaultRenderContext *rc = static_cast<QSGDefaultRenderContext *>(d->context); |
| 3462 | QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer(); |
| 3463 | if (cb) |
| 3464 | cb->endExternal(); |
| 3465 | } |
| 3466 | } |
| 3467 | |
| 3468 | /*! |
| 3469 | \qmlproperty string Window::title |
| 3470 | |
| 3471 | The window's title in the windowing system. |
| 3472 | |
| 3473 | The window title might appear in the title area of the window decorations, |
| 3474 | depending on the windowing system and the window flags. It might also |
| 3475 | be used by the windowing system to identify the window in other contexts, |
| 3476 | such as in the task switcher. |
| 3477 | */ |
| 3478 | |
| 3479 | /*! |
| 3480 | \qmlproperty Qt::WindowModality Window::modality |
| 3481 | |
| 3482 | The modality of the window. |
| 3483 | |
| 3484 | A modal window prevents other windows from receiving input events. |
| 3485 | Possible values are Qt.NonModal (the default), Qt.WindowModal, |
| 3486 | and Qt.ApplicationModal. |
| 3487 | */ |
| 3488 | |
| 3489 | /*! |
| 3490 | \qmlproperty Qt::WindowFlags Window::flags |
| 3491 | |
| 3492 | The window flags of the window. |
| 3493 | |
| 3494 | The window flags control the window's appearance in the windowing system, |
| 3495 | whether it's a dialog, popup, or a regular window, and whether it should |
| 3496 | have a title bar, etc. |
| 3497 | |
| 3498 | The flags that you read from this property might differ from the ones |
| 3499 | that you set if the requested flags could not be fulfilled. |
| 3500 | |
| 3501 | \snippet qml/splashWindow.qml entire |
| 3502 | |
| 3503 | \sa Qt::WindowFlags, {Qt Quick Examples - Window and Screen} |
| 3504 | */ |
| 3505 | |
| 3506 | /*! |
| 3507 | \qmlattachedproperty Window Window::window |
| 3508 | \since 5.7 |
| 3509 | |
| 3510 | This attached property holds the item's window. |
| 3511 | The Window attached property can be attached to any Item. |
| 3512 | */ |
| 3513 | |
| 3514 | /*! |
| 3515 | \qmlattachedproperty int Window::width |
| 3516 | \qmlattachedproperty int Window::height |
| 3517 | \since 5.5 |
| 3518 | |
| 3519 | These attached properties hold the size of the item's window. |
| 3520 | The Window attached property can be attached to any Item. |
| 3521 | */ |
| 3522 | |
| 3523 | /*! |
| 3524 | \qmlproperty int Window::x |
| 3525 | \qmlproperty int Window::y |
| 3526 | \qmlproperty int Window::width |
| 3527 | \qmlproperty int Window::height |
| 3528 | |
| 3529 | Defines the window's position and size. |
| 3530 | |
| 3531 | The (x,y) position is relative to the \l Screen if there is only one, |
| 3532 | or to the virtual desktop (arrangement of multiple screens). |
| 3533 | |
| 3534 | \note Not all windowing systems support setting or querying top level |
| 3535 | window positions. On such a system, programmatically moving windows |
| 3536 | may not have any effect, and artificial values may be returned for |
| 3537 | the current positions, such as \c QPoint(0, 0). |
| 3538 | |
| 3539 | \qml |
| 3540 | Window { x: 100; y: 100; width: 100; height: 100 } |
| 3541 | \endqml |
| 3542 | |
| 3543 | \image screen-and-window-dimensions.jpg |
| 3544 | */ |
| 3545 | |
| 3546 | /*! |
| 3547 | \qmlproperty int Window::minimumWidth |
| 3548 | \qmlproperty int Window::minimumHeight |
| 3549 | \since 5.1 |
| 3550 | |
| 3551 | Defines the window's minimum size. |
| 3552 | |
| 3553 | This is a hint to the window manager to prevent resizing below the specified |
| 3554 | width and height. |
| 3555 | */ |
| 3556 | |
| 3557 | /*! |
| 3558 | \qmlproperty int Window::maximumWidth |
| 3559 | \qmlproperty int Window::maximumHeight |
| 3560 | \since 5.1 |
| 3561 | |
| 3562 | Defines the window's maximum size. |
| 3563 | |
| 3564 | This is a hint to the window manager to prevent resizing above the specified |
| 3565 | width and height. |
| 3566 | */ |
| 3567 | |
| 3568 | /*! |
| 3569 | \qmlproperty bool Window::visible |
| 3570 | |
| 3571 | Whether the window is visible on the screen. |
| 3572 | |
| 3573 | Setting visible to false is the same as setting \l visibility to \l {QWindow::}{Hidden}. |
| 3574 | |
| 3575 | The default value is \c false, unless overridden by setting \l visibility. |
| 3576 | |
| 3577 | \sa visibility |
| 3578 | */ |
| 3579 | |
| 3580 | /*! |
| 3581 | \keyword qml-window-visibility-prop |
| 3582 | \qmlproperty QWindow::Visibility Window::visibility |
| 3583 | |
| 3584 | The screen-occupation state of the window. |
| 3585 | |
| 3586 | Visibility is whether the window should appear in the windowing system as |
| 3587 | normal, minimized, maximized, fullscreen or hidden. |
| 3588 | |
| 3589 | To set the visibility to \l {QWindow::}{AutomaticVisibility} means to give the |
| 3590 | window a default visible state, which might be \l {QWindow::}{FullScreen} or |
| 3591 | \l {QWindow::}{Windowed} depending on the platform. However when reading the |
| 3592 | visibility property you will always get the actual state, never |
| 3593 | \c AutomaticVisibility. |
| 3594 | |
| 3595 | When a window is not visible, its visibility is \c Hidden, and setting |
| 3596 | visibility to \l {QWindow::}{Hidden} is the same as setting \l visible to \c false. |
| 3597 | |
| 3598 | \snippet qml/windowVisibility.qml entire |
| 3599 | |
| 3600 | \sa visible, {Qt Quick Examples - Window and Screen} |
| 3601 | \since 5.1 |
| 3602 | */ |
| 3603 | |
| 3604 | /*! |
| 3605 | \qmlattachedproperty QWindow::Visibility Window::visibility |
| 3606 | \readonly |
| 3607 | \since 5.4 |
| 3608 | |
| 3609 | This attached property holds whether the window is currently shown |
| 3610 | in the windowing system as normal, minimized, maximized, fullscreen or |
| 3611 | hidden. The \c Window attached property can be attached to any Item. If the |
| 3612 | item is not shown in any window, the value will be \l {QWindow::}{Hidden}. |
| 3613 | |
| 3614 | \sa visible, {qml-window-visibility-prop}{visibility} |
| 3615 | */ |
| 3616 | |
| 3617 | /*! |
| 3618 | \qmlproperty Item Window::contentItem |
| 3619 | \readonly |
| 3620 | \brief The invisible root item of the scene. |
| 3621 | */ |
| 3622 | |
| 3623 | /*! |
| 3624 | \qmlproperty Qt::ScreenOrientation Window::contentOrientation |
| 3625 | |
| 3626 | This is a hint to the window manager in case it needs to display |
| 3627 | additional content like popups, dialogs, status bars, or similar |
| 3628 | in relation to the window. |
| 3629 | |
| 3630 | The recommended orientation is \l {Screen::orientation}{Screen.orientation}, but |
| 3631 | an application doesn't have to support all possible orientations, |
| 3632 | and thus can opt to ignore the current screen orientation. |
| 3633 | |
| 3634 | The difference between the window and the content orientation |
| 3635 | determines how much to rotate the content by. |
| 3636 | |
| 3637 | The default value is Qt::PrimaryOrientation. |
| 3638 | |
| 3639 | \sa Screen |
| 3640 | |
| 3641 | \since 5.1 |
| 3642 | */ |
| 3643 | |
| 3644 | /*! |
| 3645 | \qmlproperty real Window::opacity |
| 3646 | |
| 3647 | The opacity of the window. |
| 3648 | |
| 3649 | If the windowing system supports window opacity, this can be used to fade the |
| 3650 | window in and out, or to make it semitransparent. |
| 3651 | |
| 3652 | A value of 1.0 or above is treated as fully opaque, whereas a value of 0.0 or below |
| 3653 | is treated as fully transparent. Values inbetween represent varying levels of |
| 3654 | translucency between the two extremes. |
| 3655 | |
| 3656 | The default value is 1.0. |
| 3657 | |
| 3658 | \since 5.1 |
| 3659 | */ |
| 3660 | |
| 3661 | /*! |
| 3662 | \qmlproperty variant Window::screen |
| 3663 | |
| 3664 | The screen with which the window is associated. |
| 3665 | |
| 3666 | If specified before showing a window, will result in the window being shown |
| 3667 | on that screen, unless an explicit window position has been set. The value |
| 3668 | must be an element from the Qt.application.screens array. |
| 3669 | |
| 3670 | \note To ensure that the window is associated with the desired screen when |
| 3671 | the underlying native window is created, make sure this property is set as |
| 3672 | early as possible and that the setting of its value is not deferred. This |
| 3673 | can be particularly important on embedded platforms without a windowing system, |
| 3674 | where only one window per screen is allowed at a time. Setting the screen after |
| 3675 | a window has been created does not move the window if the new screen is part of |
| 3676 | the same virtual desktop as the old screen. |
| 3677 | |
| 3678 | \since 5.9 |
| 3679 | |
| 3680 | \sa QWindow::setScreen(), QWindow::screen(), QScreen, {QtQml::Qt::application}{Qt.application} |
| 3681 | */ |
| 3682 | |
| 3683 | /*! |
| 3684 | \qmlproperty QWindow Window::transientParent |
| 3685 | \since 5.13 |
| 3686 | |
| 3687 | The window for which this window is a transient pop-up. |
| 3688 | |
| 3689 | This is a hint to the window manager that this window is a dialog or pop-up |
| 3690 | on behalf of the transient parent. It usually means that the transient |
| 3691 | window will be centered over its transient parent when it is initially |
| 3692 | shown, that minimizing the parent window will also minimize the transient |
| 3693 | window, and so on; however results vary somewhat from platform to platform. |
| 3694 | |
| 3695 | Declaring a Window inside an Item or another Window, either via the |
| 3696 | \l{Window::data}{default property} or a dedicated property, will automatically |
| 3697 | set up a transient parent relationship to the containing window, |
| 3698 | unless the \l transientParent property is explicitly set. This applies |
| 3699 | when creating Window items via \l [QML] {QtQml::Qt::createComponent()} |
| 3700 | {Qt.createComponent} or \l [QML] {QtQml::Qt::createQmlObject()} |
| 3701 | {Qt.createQmlObject} as well, as long as an Item or Window is passed |
| 3702 | as the \c parent argument. |
| 3703 | |
| 3704 | A Window with a transient parent will not be shown until its transient |
| 3705 | parent is shown, even if the \l visible property is \c true. This also |
| 3706 | applies for the automatic transient parent relationship described above. |
| 3707 | In particular, if the Window's containing element is an Item, the window |
| 3708 | will not be shown until the containing item is added to a scene, via its |
| 3709 | \l{Concepts - Visual Parent in Qt Quick}{visual parent hierarchy}. Setting |
| 3710 | the \l transientParent to \c null will override this behavior: |
| 3711 | |
| 3712 | \snippet qml/nestedWindowTransientParent.qml 0 |
| 3713 | \snippet qml/nestedWindowTransientParent.qml 1 |
| 3714 | |
| 3715 | In order to cause the window to be centered above its transient parent by |
| 3716 | default, depending on the window manager, it may also be necessary to set |
| 3717 | the \l Window::flags property with a suitable \l Qt::WindowType (such as |
| 3718 | \c Qt::Dialog). |
| 3719 | |
| 3720 | \sa {QQuickWindow::}{parent()} |
| 3721 | */ |
| 3722 | |
| 3723 | /*! |
| 3724 | \property QQuickWindow::transientParent |
| 3725 | \brief The window for which this window is a transient pop-up. |
| 3726 | \since 5.13 |
| 3727 | |
| 3728 | This is a hint to the window manager that this window is a dialog or pop-up |
| 3729 | on behalf of the transient parent, which may be any kind of \l QWindow. |
| 3730 | |
| 3731 | In order to cause the window to be centered above its transient parent by |
| 3732 | default, depending on the window manager, it may also be necessary to set |
| 3733 | the \l flags property with a suitable \l Qt::WindowType (such as \c Qt::Dialog). |
| 3734 | |
| 3735 | \sa parent() |
| 3736 | */ |
| 3737 | |
| 3738 | /*! |
| 3739 | \qmlproperty Item Window::activeFocusItem |
| 3740 | \since 5.1 |
| 3741 | |
| 3742 | The item which currently has active focus or \c null if there is |
| 3743 | no item with active focus. |
| 3744 | */ |
| 3745 | |
| 3746 | /*! |
| 3747 | \qmlattachedproperty Item Window::activeFocusItem |
| 3748 | \since 5.4 |
| 3749 | |
| 3750 | This attached property holds the item which currently has active focus or |
| 3751 | \c null if there is no item with active focus. The Window attached property |
| 3752 | can be attached to any Item. |
| 3753 | */ |
| 3754 | |
| 3755 | /*! |
| 3756 | \qmlproperty bool Window::active |
| 3757 | \since 5.1 |
| 3758 | |
| 3759 | The active status of the window. |
| 3760 | |
| 3761 | \snippet qml/windowPalette.qml declaration-and-color |
| 3762 | \snippet qml/windowPalette.qml closing-brace |
| 3763 | |
| 3764 | \sa requestActivate() |
| 3765 | */ |
| 3766 | |
| 3767 | /*! |
| 3768 | \qmlattachedproperty bool Window::active |
| 3769 | \since 5.4 |
| 3770 | |
| 3771 | This attached property tells whether the window is active. The Window |
| 3772 | attached property can be attached to any Item. |
| 3773 | |
| 3774 | Here is an example which changes a label to show the active state of the |
| 3775 | window in which it is shown: |
| 3776 | |
| 3777 | \snippet qml/windowActiveAttached.qml entire |
| 3778 | */ |
| 3779 | |
| 3780 | /*! |
| 3781 | \qmlmethod QtQuick::Window::requestActivate() |
| 3782 | \since 5.1 |
| 3783 | |
| 3784 | Requests the window to be activated, i.e. receive keyboard focus. |
| 3785 | */ |
| 3786 | |
| 3787 | /*! |
| 3788 | \qmlmethod QtQuick::Window::alert(int msec) |
| 3789 | \since 5.1 |
| 3790 | |
| 3791 | Causes an alert to be shown for \a msec milliseconds. If \a msec is \c 0 |
| 3792 | (the default), then the alert is shown indefinitely until the window |
| 3793 | becomes active again. |
| 3794 | |
| 3795 | In alert state, the window indicates that it demands attention, for example |
| 3796 | by flashing or bouncing the taskbar entry. |
| 3797 | */ |
| 3798 | |
| 3799 | /*! |
| 3800 | \qmlmethod QtQuick::Window::close() |
| 3801 | |
| 3802 | Closes the window. |
| 3803 | |
| 3804 | When this method is called, or when the user tries to close the window by |
| 3805 | its title bar button, the \l closing signal will be emitted. If there is no |
| 3806 | handler, or the handler does not revoke permission to close, the window |
| 3807 | will subsequently close. If the QGuiApplication::quitOnLastWindowClosed |
| 3808 | property is \c true, and there are no other windows open, the application |
| 3809 | will quit. |
| 3810 | */ |
| 3811 | |
| 3812 | /*! |
| 3813 | \qmlmethod QtQuick::Window::raise() |
| 3814 | |
| 3815 | Raises the window in the windowing system. |
| 3816 | |
| 3817 | Requests that the window be raised to appear above other windows. |
| 3818 | */ |
| 3819 | |
| 3820 | /*! |
| 3821 | \qmlmethod QtQuick::Window::lower() |
| 3822 | |
| 3823 | Lowers the window in the windowing system. |
| 3824 | |
| 3825 | Requests that the window be lowered to appear below other windows. |
| 3826 | */ |
| 3827 | |
| 3828 | /*! |
| 3829 | \qmlmethod QtQuick::Window::show() |
| 3830 | |
| 3831 | Shows the window. |
| 3832 | |
| 3833 | This is equivalent to calling showFullScreen(), showMaximized(), or showNormal(), |
| 3834 | depending on the platform's default behavior for the window type and flags. |
| 3835 | |
| 3836 | \sa showFullScreen(), showMaximized(), showNormal(), hide(), QQuickItem::flags() |
| 3837 | */ |
| 3838 | |
| 3839 | /*! |
| 3840 | \qmlmethod QtQuick::Window::hide() |
| 3841 | |
| 3842 | Hides the window. |
| 3843 | |
| 3844 | Equivalent to setting \l visible to \c false or \l visibility to \l {QWindow::}{Hidden}. |
| 3845 | |
| 3846 | \sa show() |
| 3847 | */ |
| 3848 | |
| 3849 | /*! |
| 3850 | \qmlmethod QtQuick::Window::showMinimized() |
| 3851 | |
| 3852 | Shows the window as minimized. |
| 3853 | |
| 3854 | Equivalent to setting \l visibility to \l {QWindow::}{Minimized}. |
| 3855 | */ |
| 3856 | |
| 3857 | /*! |
| 3858 | \qmlmethod QtQuick::Window::showMaximized() |
| 3859 | |
| 3860 | Shows the window as maximized. |
| 3861 | |
| 3862 | Equivalent to setting \l visibility to \l {QWindow::}{Maximized}. |
| 3863 | */ |
| 3864 | |
| 3865 | /*! |
| 3866 | \qmlmethod QtQuick::Window::showFullScreen() |
| 3867 | |
| 3868 | Shows the window as fullscreen. |
| 3869 | |
| 3870 | Equivalent to setting \l visibility to \l {QWindow::}{FullScreen}. |
| 3871 | */ |
| 3872 | |
| 3873 | /*! |
| 3874 | \qmlmethod QtQuick::Window::showNormal() |
| 3875 | |
| 3876 | Shows the window as normal, i.e. neither maximized, minimized, nor fullscreen. |
| 3877 | |
| 3878 | Equivalent to setting \l visibility to \l {QWindow::}{Windowed}. |
| 3879 | */ |
| 3880 | |
| 3881 | /*! |
| 3882 | \enum QQuickWindow::RenderStage |
| 3883 | \since 5.4 |
| 3884 | |
| 3885 | \value BeforeSynchronizingStage Before synchronization. |
| 3886 | \value AfterSynchronizingStage After synchronization. |
| 3887 | \value BeforeRenderingStage Before rendering. |
| 3888 | \value AfterRenderingStage After rendering. |
| 3889 | \value AfterSwapStage After the frame is swapped. |
| 3890 | \value NoStage As soon as possible. This value was added in Qt 5.6. |
| 3891 | |
| 3892 | \sa {Scene Graph and Rendering} |
| 3893 | */ |
| 3894 | |
| 3895 | /*! |
| 3896 | \since 5.4 |
| 3897 | |
| 3898 | Schedules \a job to run when the rendering of this window reaches |
| 3899 | the given \a stage. |
| 3900 | |
| 3901 | This is a convenience to the equivalent signals in QQuickWindow for |
| 3902 | "one shot" tasks. |
| 3903 | |
| 3904 | The window takes ownership over \a job and will delete it when the |
| 3905 | job is completed. |
| 3906 | |
| 3907 | If rendering is shut down before \a job has a chance to run, the |
| 3908 | job will be run and then deleted as part of the scene graph cleanup. |
| 3909 | If the window is never shown and no rendering happens before the QQuickWindow |
| 3910 | is destroyed, all pending jobs will be destroyed without their run() |
| 3911 | method being called. |
| 3912 | |
| 3913 | If the rendering is happening on a different thread, then the job |
| 3914 | will happen on the rendering thread. |
| 3915 | |
| 3916 | If \a stage is \l NoStage, \a job will be run at the earliest opportunity |
| 3917 | whenever the render thread is not busy rendering a frame. If the window is |
| 3918 | not exposed, and is not renderable, at the time the job is either posted or |
| 3919 | handled, the job is deleted without executing the run() method. If a |
| 3920 | non-threaded renderer is in use, the run() method of the job is executed |
| 3921 | synchronously. When rendering with OpenGL, the OpenGL context is changed to |
| 3922 | the renderer's context before executing any job, including \l NoStage jobs. |
| 3923 | |
| 3924 | \note This function does not trigger rendering; the jobs targeting any other |
| 3925 | stage than NoStage will be stored run until rendering is triggered elsewhere. |
| 3926 | To force the job to run earlier, call QQuickWindow::update(); |
| 3927 | |
| 3928 | \sa beforeRendering(), afterRendering(), beforeSynchronizing(), |
| 3929 | afterSynchronizing(), frameSwapped(), sceneGraphInvalidated() |
| 3930 | */ |
| 3931 | |
| 3932 | void QQuickWindow::scheduleRenderJob(QRunnable *job, RenderStage stage) |
| 3933 | { |
| 3934 | Q_D(QQuickWindow); |
| 3935 | |
| 3936 | d->renderJobMutex.lock(); |
| 3937 | if (stage == BeforeSynchronizingStage) { |
| 3938 | d->beforeSynchronizingJobs << job; |
| 3939 | } else if (stage == AfterSynchronizingStage) { |
| 3940 | d->afterSynchronizingJobs << job; |
| 3941 | } else if (stage == BeforeRenderingStage) { |
| 3942 | d->beforeRenderingJobs << job; |
| 3943 | } else if (stage == AfterRenderingStage) { |
| 3944 | d->afterRenderingJobs << job; |
| 3945 | } else if (stage == AfterSwapStage) { |
| 3946 | d->afterSwapJobs << job; |
| 3947 | } else if (stage == NoStage) { |
| 3948 | if (d->renderControl && d->rhi && d->rhi->thread() == QThread::currentThread()) { |
| 3949 | job->run(); |
| 3950 | delete job; |
| 3951 | } else if (isExposed()) { |
| 3952 | d->windowManager->postJob(window: this, job); |
| 3953 | } else { |
| 3954 | delete job; |
| 3955 | } |
| 3956 | } |
| 3957 | d->renderJobMutex.unlock(); |
| 3958 | } |
| 3959 | |
| 3960 | void QQuickWindowPrivate::runAndClearJobs(QList<QRunnable *> *jobs) |
| 3961 | { |
| 3962 | renderJobMutex.lock(); |
| 3963 | QList<QRunnable *> jobList = *jobs; |
| 3964 | jobs->clear(); |
| 3965 | renderJobMutex.unlock(); |
| 3966 | |
| 3967 | for (QRunnable *r : std::as_const(t&: jobList)) { |
| 3968 | r->run(); |
| 3969 | delete r; |
| 3970 | } |
| 3971 | } |
| 3972 | |
| 3973 | void QQuickWindow::runJobsAfterSwap() |
| 3974 | { |
| 3975 | Q_D(QQuickWindow); |
| 3976 | d->runAndClearJobs(jobs: &d->afterSwapJobs); |
| 3977 | } |
| 3978 | |
| 3979 | /*! |
| 3980 | Returns the device pixel ratio for this window. |
| 3981 | |
| 3982 | This is different from QWindow::devicePixelRatio() in that it supports |
| 3983 | redirected rendering via QQuickRenderControl and QQuickRenderTarget. When |
| 3984 | using a QQuickRenderControl, the QQuickWindow is often not fully created, |
| 3985 | meaning it is never shown and there is no underlying native window created |
| 3986 | in the windowing system. As a result, querying properties like the device |
| 3987 | pixel ratio cannot give correct results. This function takes into account |
| 3988 | both QQuickRenderControl::renderWindowFor() and |
| 3989 | QQuickRenderTarget::devicePixelRatio(). When no redirection is in effect, |
| 3990 | the result is same as QWindow::devicePixelRatio(). |
| 3991 | |
| 3992 | \sa QQuickRenderControl, QQuickRenderTarget, setRenderTarget(), QWindow::devicePixelRatio() |
| 3993 | */ |
| 3994 | qreal QQuickWindow::effectiveDevicePixelRatio() const |
| 3995 | { |
| 3996 | Q_D(const QQuickWindow); |
| 3997 | QWindow *w = QQuickRenderControl::renderWindowFor(win: const_cast<QQuickWindow *>(this)); |
| 3998 | if (w) |
| 3999 | return w->devicePixelRatio(); |
| 4000 | |
| 4001 | if (!d->customRenderTarget.isNull()) |
| 4002 | return d->customRenderTarget.devicePixelRatio(); |
| 4003 | |
| 4004 | return devicePixelRatio(); |
| 4005 | } |
| 4006 | |
| 4007 | /*! |
| 4008 | \return the current renderer interface. The value is always valid and is never null. |
| 4009 | |
| 4010 | \note This function can be called at any time after constructing the |
| 4011 | QQuickWindow, even while isSceneGraphInitialized() is still false. However, |
| 4012 | some renderer interface functions, in particular |
| 4013 | QSGRendererInterface::getResource() will not be functional until the |
| 4014 | scenegraph is up and running. Backend queries, like |
| 4015 | QSGRendererInterface::graphicsApi() or QSGRendererInterface::shaderType(), |
| 4016 | will always be functional on the other hand. |
| 4017 | |
| 4018 | \note The ownership of the returned pointer stays with Qt. The returned |
| 4019 | instance may or may not be shared between different QQuickWindow instances, |
| 4020 | depending on the scenegraph backend in use. Therefore applications are |
| 4021 | expected to query the interface object for each QQuickWindow instead of |
| 4022 | reusing the already queried pointer. |
| 4023 | |
| 4024 | \sa QSGRenderNode, QSGRendererInterface |
| 4025 | |
| 4026 | \since 5.8 |
| 4027 | */ |
| 4028 | QSGRendererInterface *QQuickWindow::rendererInterface() const |
| 4029 | { |
| 4030 | Q_D(const QQuickWindow); |
| 4031 | |
| 4032 | // no context validity check - it is essential to be able to return a |
| 4033 | // renderer interface instance before scenegraphInitialized() is emitted |
| 4034 | // (depending on the backend, that can happen way too late for some of the |
| 4035 | // rif use cases, like examining the graphics api or shading language in |
| 4036 | // use) |
| 4037 | |
| 4038 | return d->context->sceneGraphContext()->rendererInterface(renderContext: d->context); |
| 4039 | } |
| 4040 | |
| 4041 | /*! |
| 4042 | \return the QRhi object used by this window for rendering. |
| 4043 | |
| 4044 | Available only when the window is using Qt's 3D API and shading language |
| 4045 | abstractions, meaning the result is always null when using the \c software |
| 4046 | adaptation. |
| 4047 | |
| 4048 | The result is valid only when rendering has been initialized, which is |
| 4049 | indicated by the emission of the sceneGraphInitialized() signal. Before |
| 4050 | that point, the returned value is null. With a regular, on-screen |
| 4051 | QQuickWindow scenegraph initialization typically happens when the native |
| 4052 | window gets exposed (shown) the first time. When using QQuickRenderControl, |
| 4053 | initialization is done in the explicit |
| 4054 | \l{QQuickRenderControl::initialize()}{initialize()} call. |
| 4055 | |
| 4056 | In practice this function is a shortcut to querying the QRhi via the |
| 4057 | QSGRendererInterface. |
| 4058 | |
| 4059 | \since 6.6 |
| 4060 | */ |
| 4061 | QRhi *QQuickWindow::rhi() const |
| 4062 | { |
| 4063 | Q_D(const QQuickWindow); |
| 4064 | return d->rhi; |
| 4065 | } |
| 4066 | |
| 4067 | /*! |
| 4068 | \return the QRhiSwapChain used by this window, if there is one. |
| 4069 | |
| 4070 | \note Only on-screen windows backed by one of the standard render loops |
| 4071 | (such as, \c basic or \c threaded) will have a swapchain. Otherwise the |
| 4072 | returned value is null. For example, the result is always null when the |
| 4073 | window is used with QQuickRenderControl. |
| 4074 | |
| 4075 | \since 6.6 |
| 4076 | */ |
| 4077 | QRhiSwapChain *QQuickWindow::swapChain() const |
| 4078 | { |
| 4079 | Q_D(const QQuickWindow); |
| 4080 | return d->swapchain; |
| 4081 | } |
| 4082 | |
| 4083 | /*! |
| 4084 | Requests the specified graphics \a api. |
| 4085 | |
| 4086 | When the built-in, default graphics adaptation is used, \a api specifies |
| 4087 | which graphics API (OpenGL, Vulkan, Metal, or Direct3D) the scene graph |
| 4088 | should use to render. In addition, the \c software backend is built-in as |
| 4089 | well, and can be requested by setting \a api to |
| 4090 | QSGRendererInterface::Software. |
| 4091 | |
| 4092 | Unlike setSceneGraphBackend(), which can only be used to request a given |
| 4093 | backend (shipped either built-in or installed as dynamically loaded |
| 4094 | plugins), this function works with the higher level concept of graphics |
| 4095 | APIs. It covers the backends that ship with Qt Quick, and thus have |
| 4096 | corresponding values in the QSGRendererInterface::GraphicsApi enum. |
| 4097 | |
| 4098 | When this function is not called at all, and the equivalent environment |
| 4099 | variable \c{QSG_RHI_BACKEND} is not set either, the scene graph will choose |
| 4100 | the graphics API to use based on the platform. |
| 4101 | |
| 4102 | This function becomes important in applications that are only prepared for |
| 4103 | rendering with a given API. For example, if there is native OpenGL or |
| 4104 | Vulkan rendering done by the application, it will want to ensure Qt Quick |
| 4105 | is rendering using OpenGL or Vulkan too. Such applications are expected to |
| 4106 | call this function early in their main() function. |
| 4107 | |
| 4108 | \note The call to the function must happen before constructing the first |
| 4109 | QQuickWindow in the application. The graphics API cannot be changed |
| 4110 | afterwards. |
| 4111 | |
| 4112 | \note When used in combination with QQuickRenderControl, this rule is |
| 4113 | relaxed: it is possible to change the graphics API, but only when all |
| 4114 | existing QQuickRenderControl and QQuickWindow instances have been |
| 4115 | destroyed. |
| 4116 | |
| 4117 | To query what graphics API the scene graph is using to render, |
| 4118 | QSGRendererInterface::graphicsApi() after the scene graph |
| 4119 | \l{QQuickWindow::isSceneGraphInitialized()}{has initialized}, which |
| 4120 | typically happens either when the window becomes visible for the first time, or |
| 4121 | when QQuickRenderControl::initialize() is called. |
| 4122 | |
| 4123 | To switch back to the default behavior, where the scene graph chooses a |
| 4124 | graphics API based on the platform and other conditions, set \a api to |
| 4125 | QSGRendererInterface::Unknown. |
| 4126 | |
| 4127 | \since 6.0 |
| 4128 | */ |
| 4129 | void QQuickWindow::setGraphicsApi(QSGRendererInterface::GraphicsApi api) |
| 4130 | { |
| 4131 | // Special cases: these are different scenegraph backends. |
| 4132 | switch (api) { |
| 4133 | case QSGRendererInterface::Software: |
| 4134 | setSceneGraphBackend(QStringLiteral("software" )); |
| 4135 | break; |
| 4136 | case QSGRendererInterface::OpenVG: |
| 4137 | setSceneGraphBackend(QStringLiteral("openvg" )); |
| 4138 | break; |
| 4139 | default: |
| 4140 | break; |
| 4141 | } |
| 4142 | |
| 4143 | // Standard case: tell the QRhi-based default adaptation what graphics api |
| 4144 | // (QRhi backend) to use. |
| 4145 | if (QSGRendererInterface::isApiRhiBased(api) || api == QSGRendererInterface::Unknown) |
| 4146 | QSGRhiSupport::instance_internal()->configure(api); |
| 4147 | } |
| 4148 | |
| 4149 | /*! |
| 4150 | \return the graphics API that would be used by the scene graph if it was |
| 4151 | initialized at this point in time. |
| 4152 | |
| 4153 | The standard way to query the API used by the scene graph is to use |
| 4154 | QSGRendererInterface::graphicsApi() once the scene graph has initialized, |
| 4155 | for example when or after the sceneGraphInitialized() signal is emitted. In |
| 4156 | that case one gets the true, real result, because then it is known that |
| 4157 | everything was initialized correctly using that graphics API. |
| 4158 | |
| 4159 | This is not always convenient. If the application needs to set up external |
| 4160 | frameworks, or needs to work with setGraphicsDevice() in a manner that |
| 4161 | depends on the scene graph's built in API selection logic, it is not always |
| 4162 | feasiable to defer such operations until after the QQuickWindow has been |
| 4163 | made visible or QQuickRenderControl::initialize() has been called. |
| 4164 | |
| 4165 | Therefore, this static function is provided as a counterpart to |
| 4166 | setGraphicsApi(): it can be called at any time, and the result reflects |
| 4167 | what API the scene graph would choose if it was initialized at the point of |
| 4168 | the call. |
| 4169 | |
| 4170 | \note This static function is intended to be called on the main (GUI) |
| 4171 | thread only. For querying the API when rendering, use QSGRendererInterface |
| 4172 | since that object lives on the render thread. |
| 4173 | |
| 4174 | \note This function does not take scene graph backends into account. |
| 4175 | |
| 4176 | \since 6.0 |
| 4177 | */ |
| 4178 | QSGRendererInterface::GraphicsApi QQuickWindow::graphicsApi() |
| 4179 | { |
| 4180 | // Note that this applies the settings e.g. from the env vars |
| 4181 | // (QSG_RHI_BACKEND) if it was not done at least once already. Whereas if |
| 4182 | // setGraphicsApi() was called before, or the scene graph is already |
| 4183 | // initialized, then this is just a simple query. |
| 4184 | return QSGRhiSupport::instance()->graphicsApi(); |
| 4185 | } |
| 4186 | |
| 4187 | /*! |
| 4188 | Requests a Qt Quick scenegraph \a backend. Backends can either be built-in |
| 4189 | or be installed in form of dynamically loaded plugins. |
| 4190 | |
| 4191 | \overload |
| 4192 | |
| 4193 | \note The call to the function must happen before constructing the first |
| 4194 | QQuickWindow in the application. It cannot be changed afterwards. |
| 4195 | |
| 4196 | See \l{Switch Between Adaptations in Your Application} for more information |
| 4197 | about the list of backends. If \a backend is invalid or an error occurs, the |
| 4198 | request is ignored. |
| 4199 | |
| 4200 | \note Calling this function is equivalent to setting the |
| 4201 | \c QT_QUICK_BACKEND or \c QMLSCENE_DEVICE environment variables. However, this |
| 4202 | API is safer to use in applications that spawn other processes as there is |
| 4203 | no need to worry about environment inheritance. |
| 4204 | |
| 4205 | \since 5.8 |
| 4206 | */ |
| 4207 | void QQuickWindow::setSceneGraphBackend(const QString &backend) |
| 4208 | { |
| 4209 | QSGContext::setBackend(backend); |
| 4210 | } |
| 4211 | |
| 4212 | /*! |
| 4213 | Returns the requested Qt Quick scenegraph backend. |
| 4214 | |
| 4215 | \note The return value of this function may still be outdated by |
| 4216 | subsequent calls to setSceneGraphBackend() until the first QQuickWindow in the |
| 4217 | application has been constructed. |
| 4218 | |
| 4219 | \note The value only reflects the request in the \c{QT_QUICK_BACKEND} |
| 4220 | environment variable after a QQuickWindow has been constructed. |
| 4221 | |
| 4222 | \since 5.9 |
| 4223 | */ |
| 4224 | QString QQuickWindow::sceneGraphBackend() |
| 4225 | { |
| 4226 | return QSGContext::backend(); |
| 4227 | } |
| 4228 | |
| 4229 | /*! |
| 4230 | Sets the graphics device objects for this window. The scenegraph will use |
| 4231 | existing device, physical device, and other objects specified by \a device |
| 4232 | instead of creating new ones. |
| 4233 | |
| 4234 | This function is very often used in combination with QQuickRenderControl |
| 4235 | and setRenderTarget(), in order to redirect Qt Quick rendering into a |
| 4236 | texture. |
| 4237 | |
| 4238 | A default constructed QQuickGraphicsDevice does not change the default |
| 4239 | behavior in any way. Once a \a device created via one of the |
| 4240 | QQuickGraphicsDevice factory functions, such as, |
| 4241 | QQuickGraphicsDevice::fromDeviceObjects(), is passed in, and the scenegraph |
| 4242 | uses a matching graphics API (with the example of fromDeviceObjects(), that |
| 4243 | would be Vulkan), the scenegraph will use the existing device objects (such |
| 4244 | as, the \c VkPhysicalDevice, \c VkDevice, and graphics queue family index, |
| 4245 | in case of Vulkan) encapsulated by the QQuickGraphicsDevice. This allows |
| 4246 | using the same device, and so sharing resources, such as buffers and |
| 4247 | textures, between Qt Quick and native rendering engines. |
| 4248 | |
| 4249 | \warning This function can only be called before initializing the |
| 4250 | scenegraph and will have no effect if called afterwards. In practice this |
| 4251 | typically means calling it right before QQuickRenderControl::initialize(). |
| 4252 | |
| 4253 | As an example, this time with Direct3D, the typical usage is expected to be |
| 4254 | the following: |
| 4255 | |
| 4256 | \badcode |
| 4257 | // native graphics resources set up by a custom D3D rendering engine |
| 4258 | ID3D11Device *device; |
| 4259 | ID3D11DeviceContext *context; |
| 4260 | ID3D11Texture2D *texture; |
| 4261 | ... |
| 4262 | // now to redirect Qt Quick content into 'texture' we could do the following: |
| 4263 | QQuickRenderControl *renderControl = new QQuickRenderControl; |
| 4264 | QQuickWindow *window = new QQuickWindow(renderControl); // this window will never be shown on-screen |
| 4265 | ... |
| 4266 | window->setGraphicsDevice(QQuickGraphicsDevice::fromDeviceAndContext(device, context)); |
| 4267 | renderControl->initialize(); |
| 4268 | window->setRenderTarget(QQuickRenderTarget::fromD3D11Texture(texture, textureSize); |
| 4269 | ... |
| 4270 | \endcode |
| 4271 | |
| 4272 | The key aspect of using this function is to ensure that resources or |
| 4273 | handles to resources, such as \c texture in the above example, are visible |
| 4274 | to and usable by both the external rendering engine and the scenegraph |
| 4275 | renderer. This requires using the same graphics device (or with OpenGL, |
| 4276 | OpenGL context). |
| 4277 | |
| 4278 | QQuickGraphicsDevice instances are implicitly shared, copyable, and |
| 4279 | can be passed by value. They do not own the associated native objects (such |
| 4280 | as, the ID3D11Device in the example). |
| 4281 | |
| 4282 | \note Using QQuickRenderControl does not always imply having to call this |
| 4283 | function. When adopting an existing device or context is not needed, this |
| 4284 | function should not be called, and the scene graph will then initialize its |
| 4285 | own devices and contexts normally, just as it would with an on-screen |
| 4286 | QQuickWindow. |
| 4287 | |
| 4288 | \since 6.0 |
| 4289 | |
| 4290 | \sa QQuickRenderControl, setRenderTarget(), setGraphicsApi() |
| 4291 | */ |
| 4292 | void QQuickWindow::setGraphicsDevice(const QQuickGraphicsDevice &device) |
| 4293 | { |
| 4294 | Q_D(QQuickWindow); |
| 4295 | d->customDeviceObjects = device; |
| 4296 | } |
| 4297 | |
| 4298 | /*! |
| 4299 | \return the QQuickGraphicsDevice passed to setGraphicsDevice(), or a |
| 4300 | default constructed one otherwise |
| 4301 | |
| 4302 | \since 6.0 |
| 4303 | |
| 4304 | \sa setGraphicsDevice() |
| 4305 | */ |
| 4306 | QQuickGraphicsDevice QQuickWindow::graphicsDevice() const |
| 4307 | { |
| 4308 | Q_D(const QQuickWindow); |
| 4309 | return d->customDeviceObjects; |
| 4310 | } |
| 4311 | |
| 4312 | /*! |
| 4313 | Sets the graphics configuration for this window. \a config contains various |
| 4314 | settings that may be taken into account by the scene graph when |
| 4315 | initializing the underlying graphics devices and contexts. |
| 4316 | |
| 4317 | Such additional configuration, specifying for example what device |
| 4318 | extensions to enable for Vulkan, becomes relevant and essential when |
| 4319 | integrating native graphics rendering code that relies on certain |
| 4320 | extensions. The same is true when integrating with an external 3D or VR |
| 4321 | engines, such as OpenXR. |
| 4322 | |
| 4323 | \note The configuration is ignored when adopting existing graphics devices |
| 4324 | via setGraphicsDevice() since the scene graph is then not in control of the |
| 4325 | actual construction of those objects. |
| 4326 | |
| 4327 | QQuickGraphicsConfiguration instances are implicitly shared, copyable, and |
| 4328 | can be passed by value. |
| 4329 | |
| 4330 | \warning Setting a QQuickGraphicsConfiguration on a QQuickWindow must |
| 4331 | happen early enough, before the scene graph is initialized for the first |
| 4332 | time for that window. With on-screen windows this means the call must be |
| 4333 | done before invoking show() on the QQuickWindow or QQuickView. With |
| 4334 | QQuickRenderControl the configuration must be finalized before calling |
| 4335 | \l{QQuickRenderControl::initialize()}{initialize()}. |
| 4336 | |
| 4337 | \since 6.0 |
| 4338 | */ |
| 4339 | void QQuickWindow::setGraphicsConfiguration(const QQuickGraphicsConfiguration &config) |
| 4340 | { |
| 4341 | Q_D(QQuickWindow); |
| 4342 | d->graphicsConfig = config; |
| 4343 | } |
| 4344 | |
| 4345 | /*! |
| 4346 | \return the QQuickGraphicsConfiguration passed to |
| 4347 | setGraphicsConfiguration(), or a default constructed one otherwise. |
| 4348 | |
| 4349 | \since 6.0 |
| 4350 | |
| 4351 | \sa setGraphicsConfiguration() |
| 4352 | */ |
| 4353 | QQuickGraphicsConfiguration QQuickWindow::graphicsConfiguration() const |
| 4354 | { |
| 4355 | Q_D(const QQuickWindow); |
| 4356 | return d->graphicsConfig; |
| 4357 | } |
| 4358 | |
| 4359 | /*! |
| 4360 | Creates a text node. When the scenegraph is not initialized, the return value is null. |
| 4361 | |
| 4362 | \since 6.7 |
| 4363 | \sa QSGTextNode |
| 4364 | */ |
| 4365 | QSGTextNode *QQuickWindow::createTextNode() const |
| 4366 | { |
| 4367 | Q_D(const QQuickWindow); |
| 4368 | return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createTextNode(renderContext: d->context) : nullptr; |
| 4369 | } |
| 4370 | |
| 4371 | /*! |
| 4372 | Creates a simple rectangle node. When the scenegraph is not initialized, the return value is null. |
| 4373 | |
| 4374 | This is cross-backend alternative to constructing a QSGSimpleRectNode directly. |
| 4375 | |
| 4376 | \since 5.8 |
| 4377 | \sa QSGRectangleNode |
| 4378 | */ |
| 4379 | QSGRectangleNode *QQuickWindow::createRectangleNode() const |
| 4380 | { |
| 4381 | Q_D(const QQuickWindow); |
| 4382 | return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createRectangleNode() : nullptr; |
| 4383 | } |
| 4384 | |
| 4385 | /*! |
| 4386 | Creates a simple image node. When the scenegraph is not initialized, the return value is null. |
| 4387 | |
| 4388 | This is cross-backend alternative to constructing a QSGSimpleTextureNode directly. |
| 4389 | |
| 4390 | \since 5.8 |
| 4391 | \sa QSGImageNode |
| 4392 | */ |
| 4393 | QSGImageNode *QQuickWindow::createImageNode() const |
| 4394 | { |
| 4395 | Q_D(const QQuickWindow); |
| 4396 | return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createImageNode() : nullptr; |
| 4397 | } |
| 4398 | |
| 4399 | /*! |
| 4400 | Creates a nine patch node. When the scenegraph is not initialized, the return value is null. |
| 4401 | |
| 4402 | \since 5.8 |
| 4403 | */ |
| 4404 | QSGNinePatchNode *QQuickWindow::createNinePatchNode() const |
| 4405 | { |
| 4406 | Q_D(const QQuickWindow); |
| 4407 | return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createNinePatchNode() : nullptr; |
| 4408 | } |
| 4409 | |
| 4410 | /*! |
| 4411 | \since 5.10 |
| 4412 | |
| 4413 | Returns the render type of text-like elements in Qt Quick. |
| 4414 | The default is QQuickWindow::QtTextRendering. |
| 4415 | |
| 4416 | \sa setTextRenderType() |
| 4417 | */ |
| 4418 | QQuickWindow::TextRenderType QQuickWindow::textRenderType() |
| 4419 | { |
| 4420 | return QQuickWindowPrivate::textRenderType; |
| 4421 | } |
| 4422 | |
| 4423 | /*! |
| 4424 | \since 5.10 |
| 4425 | |
| 4426 | Sets the default render type of text-like elements in Qt Quick to \a renderType. |
| 4427 | |
| 4428 | \note setting the render type will only affect elements created afterwards; |
| 4429 | the render type of existing elements will not be modified. |
| 4430 | |
| 4431 | \sa textRenderType() |
| 4432 | */ |
| 4433 | void QQuickWindow::setTextRenderType(QQuickWindow::TextRenderType renderType) |
| 4434 | { |
| 4435 | QQuickWindowPrivate::textRenderType = renderType; |
| 4436 | } |
| 4437 | |
| 4438 | |
| 4439 | /*! |
| 4440 | \since 6.0 |
| 4441 | \qmlproperty Palette Window::palette |
| 4442 | |
| 4443 | This property holds the palette currently set for the window. |
| 4444 | |
| 4445 | The default palette depends on the system environment. QGuiApplication maintains a system/theme |
| 4446 | palette which serves as a default for all application windows. You can also set the default palette |
| 4447 | for windows by passing a custom palette to QGuiApplication::setPalette(), before loading any QML. |
| 4448 | |
| 4449 | Window propagates explicit palette properties to child items and controls, |
| 4450 | overriding any system defaults for that property. |
| 4451 | |
| 4452 | \snippet qml/windowPalette.qml entire |
| 4453 | |
| 4454 | \sa Item::palette, Popup::palette, ColorGroup, SystemPalette |
| 4455 | //! internal \sa QQuickAbstractPaletteProvider, QQuickPalette |
| 4456 | */ |
| 4457 | |
| 4458 | #ifndef QT_NO_DEBUG_STREAM |
| 4459 | QDebug operator<<(QDebug debug, const QQuickWindow *win) |
| 4460 | { |
| 4461 | QDebugStateSaver saver(debug); |
| 4462 | debug.nospace(); |
| 4463 | if (!win) { |
| 4464 | debug << "QQuickWindow(nullptr)" ; |
| 4465 | return debug; |
| 4466 | } |
| 4467 | |
| 4468 | debug << win->metaObject()->className() << '(' << static_cast<const void *>(win); |
| 4469 | if (win->isActive()) |
| 4470 | debug << " active" ; |
| 4471 | if (win->isExposed()) |
| 4472 | debug << " exposed" ; |
| 4473 | debug << ", visibility=" << win->visibility() << ", flags=" << win->flags(); |
| 4474 | if (!win->title().isEmpty()) |
| 4475 | debug << ", title=" << win->title(); |
| 4476 | if (!win->objectName().isEmpty()) |
| 4477 | debug << ", name=" << win->objectName(); |
| 4478 | if (win->parent()) |
| 4479 | debug << ", parent=" << static_cast<const void *>(win->parent()); |
| 4480 | if (win->transientParent()) |
| 4481 | debug << ", transientParent=" << static_cast<const void *>(win->transientParent()); |
| 4482 | debug << ", geometry=" ; |
| 4483 | QtDebugUtils::formatQRect(debug, rect: win->geometry()); |
| 4484 | debug << ')'; |
| 4485 | return debug; |
| 4486 | } |
| 4487 | #endif |
| 4488 | |
| 4489 | QT_END_NAMESPACE |
| 4490 | |
| 4491 | #include "qquickwindow.moc" |
| 4492 | #include "moc_qquickwindow_p.cpp" |
| 4493 | #include "moc_qquickwindow.cpp" |
| 4494 | |