| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2018 The Qt Company Ltd. |
| 4 | ** Contact: https://www.qt.io/licensing/ |
| 5 | ** |
| 6 | ** This file is part of the QtQuick module of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:LGPL$ |
| 9 | ** Commercial License Usage |
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in |
| 11 | ** accordance with the commercial license agreement provided with the |
| 12 | ** Software or, alternatively, in accordance with the terms contained in |
| 13 | ** a written agreement between you and The Qt Company. For licensing terms |
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
| 15 | ** information use the contact form at https://www.qt.io/contact-us. |
| 16 | ** |
| 17 | ** GNU Lesser General Public License Usage |
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
| 19 | ** General Public License version 3 as published by the Free Software |
| 20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| 21 | ** packaging of this file. Please review the following information to |
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements |
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| 24 | ** |
| 25 | ** GNU General Public License Usage |
| 26 | ** Alternatively, this file may be used under the terms of the GNU |
| 27 | ** General Public License version 2.0 or (at your option) the GNU General |
| 28 | ** Public license version 3 or any later version approved by the KDE Free |
| 29 | ** Qt Foundation. The licenses are as published by the Free Software |
| 30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| 31 | ** included in the packaging of this file. Please review the following |
| 32 | ** information to ensure the GNU General Public License requirements will |
| 33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| 34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
| 35 | ** |
| 36 | ** $QT_END_LICENSE$ |
| 37 | ** |
| 38 | ****************************************************************************/ |
| 39 | |
| 40 | #include "qquickwindow.h" |
| 41 | #include "qquickwindow_p.h" |
| 42 | |
| 43 | #include "qquickitem.h" |
| 44 | #include "qquickitem_p.h" |
| 45 | #include "qquickevents_p_p.h" |
| 46 | |
| 47 | #if QT_CONFIG(quick_draganddrop) |
| 48 | #include <private/qquickdrag_p.h> |
| 49 | #endif |
| 50 | #include <private/qquickhoverhandler_p.h> |
| 51 | #include <private/qquickpointerhandler_p.h> |
| 52 | |
| 53 | #include <QtQuick/private/qsgrenderer_p.h> |
| 54 | #include <QtQuick/private/qsgplaintexture_p.h> |
| 55 | #include <private/qsgrenderloop_p.h> |
| 56 | #include <private/qsgrhisupport_p.h> |
| 57 | #include <private/qquickrendercontrol_p.h> |
| 58 | #include <private/qquickanimatorcontroller_p.h> |
| 59 | #include <private/qquickprofiler_p.h> |
| 60 | |
| 61 | #include <private/qguiapplication_p.h> |
| 62 | #include <QtGui/QInputMethod> |
| 63 | |
| 64 | #include <private/qabstractanimation_p.h> |
| 65 | |
| 66 | #include <QtGui/qpainter.h> |
| 67 | #include <QtGui/qevent.h> |
| 68 | #include <QtGui/qmatrix4x4.h> |
| 69 | #include <QtGui/qpa/qplatformtheme.h> |
| 70 | #include <QtCore/qvarlengtharray.h> |
| 71 | #include <QtCore/qabstractanimation.h> |
| 72 | #include <QtCore/QLibraryInfo> |
| 73 | #include <QtCore/QRunnable> |
| 74 | #include <QtQml/qqmlincubator.h> |
| 75 | #include <QtQml/qqmlinfo.h> |
| 76 | #include <QtQml/private/qqmlmetatype_p.h> |
| 77 | |
| 78 | #include <QtQuick/private/qquickpixmapcache_p.h> |
| 79 | |
| 80 | #include <private/qqmldebugserviceinterfaces_p.h> |
| 81 | #include <private/qqmldebugconnector_p.h> |
| 82 | #if QT_CONFIG(opengl) |
| 83 | # include <private/qopenglvertexarrayobject_p.h> |
| 84 | # include <private/qsgdefaultrendercontext_p.h> |
| 85 | #endif |
| 86 | #ifndef QT_NO_DEBUG_STREAM |
| 87 | #include <private/qdebug_p.h> |
| 88 | #endif |
| 89 | |
| 90 | #include <QtGui/private/qrhi_p.h> |
| 91 | |
| 92 | QT_BEGIN_NAMESPACE |
| 93 | |
| 94 | Q_LOGGING_CATEGORY(DBG_TOUCH, "qt.quick.touch" ) |
| 95 | Q_LOGGING_CATEGORY(DBG_TOUCH_TARGET, "qt.quick.touch.target" ) |
| 96 | Q_LOGGING_CATEGORY(DBG_MOUSE, "qt.quick.mouse" ) |
| 97 | Q_LOGGING_CATEGORY(DBG_MOUSE_TARGET, "qt.quick.mouse.target" ) |
| 98 | Q_LOGGING_CATEGORY(lcTablet, "qt.quick.tablet" ) |
| 99 | Q_LOGGING_CATEGORY(lcWheelTarget, "qt.quick.wheel.target" ) |
| 100 | Q_LOGGING_CATEGORY(lcGestureTarget, "qt.quick.gesture.target" ) |
| 101 | Q_LOGGING_CATEGORY(DBG_HOVER_TRACE, "qt.quick.hover.trace" ) |
| 102 | Q_LOGGING_CATEGORY(DBG_FOCUS, "qt.quick.focus" ) |
| 103 | Q_LOGGING_CATEGORY(DBG_DIRTY, "qt.quick.dirty" ) |
| 104 | Q_LOGGING_CATEGORY(lcTransient, "qt.quick.window.transient" ) |
| 105 | |
| 106 | extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); |
| 107 | extern Q_GUI_EXPORT bool qt_sendShortcutOverrideEvent(QObject *o, ulong timestamp, int k, Qt::KeyboardModifiers mods, const QString &text = QString(), bool autorep = false, ushort count = 1); |
| 108 | |
| 109 | bool QQuickWindowPrivate::defaultAlphaBuffer = false; |
| 110 | |
| 111 | #if defined(QT_QUICK_DEFAULT_TEXT_RENDER_TYPE) |
| 112 | QQuickWindow::TextRenderType QQuickWindowPrivate::textRenderType = QQuickWindow::QT_QUICK_DEFAULT_TEXT_RENDER_TYPE; |
| 113 | #else |
| 114 | QQuickWindow::TextRenderType QQuickWindowPrivate::textRenderType = QQuickWindow::QtTextRendering; |
| 115 | #endif |
| 116 | |
| 117 | void QQuickWindowPrivate::updateFocusItemTransform() |
| 118 | { |
| 119 | #if QT_CONFIG(im) |
| 120 | Q_Q(QQuickWindow); |
| 121 | QQuickItem *focus = q->activeFocusItem(); |
| 122 | if (focus && QGuiApplication::focusObject() == focus) { |
| 123 | QQuickItemPrivate *focusPrivate = QQuickItemPrivate::get(item: focus); |
| 124 | QGuiApplication::inputMethod()->setInputItemTransform(focusPrivate->itemToWindowTransform()); |
| 125 | QGuiApplication::inputMethod()->setInputItemRectangle(QRectF(0, 0, focusPrivate->width, focusPrivate->height)); |
| 126 | focus->updateInputMethod(queries: Qt::ImInputItemClipRectangle); |
| 127 | } |
| 128 | #endif |
| 129 | } |
| 130 | |
| 131 | class QQuickWindowIncubationController : public QObject, public QQmlIncubationController |
| 132 | { |
| 133 | Q_OBJECT |
| 134 | |
| 135 | public: |
| 136 | QQuickWindowIncubationController(QSGRenderLoop *loop) |
| 137 | : m_renderLoop(loop), m_timer(0) |
| 138 | { |
| 139 | // Allow incubation for 1/3 of a frame. |
| 140 | m_incubation_time = qMax(a: 1, b: int(1000 / QGuiApplication::primaryScreen()->refreshRate()) / 3); |
| 141 | |
| 142 | QAnimationDriver *animationDriver = m_renderLoop->animationDriver(); |
| 143 | if (animationDriver) { |
| 144 | connect(sender: animationDriver, SIGNAL(stopped()), receiver: this, SLOT(animationStopped())); |
| 145 | connect(sender: m_renderLoop, SIGNAL(timeToIncubate()), receiver: this, SLOT(incubate())); |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | protected: |
| 150 | void timerEvent(QTimerEvent *) override |
| 151 | { |
| 152 | killTimer(id: m_timer); |
| 153 | m_timer = 0; |
| 154 | incubate(); |
| 155 | } |
| 156 | |
| 157 | void incubateAgain() { |
| 158 | if (m_timer == 0) { |
| 159 | // Wait for a while before processing the next batch. Using a |
| 160 | // timer to avoid starvation of system events. |
| 161 | m_timer = startTimer(interval: m_incubation_time); |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | public slots: |
| 166 | void incubate() { |
| 167 | if (m_renderLoop && incubatingObjectCount()) { |
| 168 | if (m_renderLoop->interleaveIncubation()) { |
| 169 | incubateFor(msecs: m_incubation_time); |
| 170 | } else { |
| 171 | incubateFor(msecs: m_incubation_time * 2); |
| 172 | if (incubatingObjectCount()) |
| 173 | incubateAgain(); |
| 174 | } |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | void animationStopped() { incubate(); } |
| 179 | |
| 180 | protected: |
| 181 | void incubatingObjectCountChanged(int count) override |
| 182 | { |
| 183 | if (count && m_renderLoop && !m_renderLoop->interleaveIncubation()) |
| 184 | incubateAgain(); |
| 185 | } |
| 186 | |
| 187 | private: |
| 188 | QPointer<QSGRenderLoop> m_renderLoop; |
| 189 | int m_incubation_time; |
| 190 | int m_timer; |
| 191 | }; |
| 192 | |
| 193 | #include "qquickwindow.moc" |
| 194 | #include "moc_qquickwindow_p.cpp" |
| 195 | |
| 196 | |
| 197 | #if QT_CONFIG(accessibility) |
| 198 | /*! |
| 199 | Returns an accessibility interface for this window, or 0 if such an |
| 200 | interface cannot be created. |
| 201 | */ |
| 202 | QAccessibleInterface *QQuickWindow::accessibleRoot() const |
| 203 | { |
| 204 | return QAccessible::queryAccessibleInterface(const_cast<QQuickWindow*>(this)); |
| 205 | } |
| 206 | #endif |
| 207 | |
| 208 | |
| 209 | /* |
| 210 | Focus behavior |
| 211 | ============== |
| 212 | |
| 213 | Prior to being added to a valid window items can set and clear focus with no |
| 214 | effect. Only once items are added to a window (by way of having a parent set that |
| 215 | already belongs to a window) do the focus rules apply. Focus goes back to |
| 216 | having no effect if an item is removed from a window. |
| 217 | |
| 218 | When an item is moved into a new focus scope (either being added to a window |
| 219 | for the first time, or having its parent changed), if the focus scope already has |
| 220 | a scope focused item that takes precedence over the item being added. Otherwise, |
| 221 | the focus of the added tree is used. In the case of a tree of items being |
| 222 | added to a window for the first time, which may have a conflicted focus state (two |
| 223 | or more items in one scope having focus set), the same rule is applied item by item - |
| 224 | thus the first item that has focus will get it (assuming the scope doesn't already |
| 225 | have a scope focused item), and the other items will have their focus cleared. |
| 226 | */ |
| 227 | |
| 228 | QQuickRootItem::QQuickRootItem() |
| 229 | { |
| 230 | } |
| 231 | |
| 232 | /*! \reimp */ |
| 233 | void QQuickWindow::exposeEvent(QExposeEvent *) |
| 234 | { |
| 235 | Q_D(QQuickWindow); |
| 236 | if (d->windowManager) |
| 237 | d->windowManager->exposureChanged(window: this); |
| 238 | } |
| 239 | |
| 240 | /*! \reimp */ |
| 241 | void QQuickWindow::resizeEvent(QResizeEvent *ev) |
| 242 | { |
| 243 | Q_D(QQuickWindow); |
| 244 | if (d->contentItem) |
| 245 | d->contentItem->setSize(ev->size()); |
| 246 | if (d->windowManager) |
| 247 | d->windowManager->resize(this); |
| 248 | } |
| 249 | |
| 250 | /*! \reimp */ |
| 251 | void QQuickWindow::showEvent(QShowEvent *) |
| 252 | { |
| 253 | Q_D(QQuickWindow); |
| 254 | if (d->windowManager) |
| 255 | d->windowManager->show(window: this); |
| 256 | } |
| 257 | |
| 258 | /*! \reimp */ |
| 259 | void QQuickWindow::hideEvent(QHideEvent *) |
| 260 | { |
| 261 | Q_D(QQuickWindow); |
| 262 | if (d->windowManager) |
| 263 | d->windowManager->hide(window: this); |
| 264 | } |
| 265 | |
| 266 | /*! \reimp */ |
| 267 | void QQuickWindow::focusOutEvent(QFocusEvent *ev) |
| 268 | { |
| 269 | Q_D(QQuickWindow); |
| 270 | if (d->contentItem) |
| 271 | d->contentItem->setFocus(focus: false, reason: ev->reason()); |
| 272 | } |
| 273 | |
| 274 | /*! \reimp */ |
| 275 | void QQuickWindow::focusInEvent(QFocusEvent *ev) |
| 276 | { |
| 277 | Q_D(QQuickWindow); |
| 278 | if (d->contentItem) |
| 279 | d->contentItem->setFocus(focus: true, reason: ev->reason()); |
| 280 | d->updateFocusItemTransform(); |
| 281 | } |
| 282 | |
| 283 | #if QT_CONFIG(im) |
| 284 | static bool transformDirtyOnItemOrAncestor(const QQuickItem *item) |
| 285 | { |
| 286 | while (item) { |
| 287 | if (QQuickItemPrivate::get(item)->dirtyAttributes & ( |
| 288 | QQuickItemPrivate::TransformOrigin | |
| 289 | QQuickItemPrivate::Transform | |
| 290 | QQuickItemPrivate::BasicTransform | |
| 291 | QQuickItemPrivate::Position | |
| 292 | QQuickItemPrivate::Size | |
| 293 | QQuickItemPrivate::ParentChanged | |
| 294 | QQuickItemPrivate::Clip)) { |
| 295 | return true; |
| 296 | } |
| 297 | item = item->parentItem(); |
| 298 | } |
| 299 | return false; |
| 300 | } |
| 301 | #endif |
| 302 | |
| 303 | /*! |
| 304 | * \internal |
| 305 | |
| 306 | A "polish loop" can occur inside QQuickWindowPrivate::polishItems(). It is when an item calls |
| 307 | polish() on an(other?) item from updatePolish(). If this anomaly happens repeatedly and without |
| 308 | interruption (of a well-behaved updatePolish() that doesn't call polish()), it is a strong |
| 309 | indication that we are heading towards an infinite polish loop. A polish loop is not a bug in |
| 310 | Qt Quick - it is a bug caused by ill-behaved items put in the scene. |
| 311 | |
| 312 | We can detect this sequence of polish loops easily, since the |
| 313 | QQuickWindowPrivate::itemsToPolish is basically a stack: polish() will push to it, and |
| 314 | polishItems() will pop from it. |
| 315 | Therefore if updatePolish() calls polish(), the immediate next item polishItems() processes is |
| 316 | the item that was polished by the previous call to updatePolish(). |
| 317 | We therefore just need to count the number of polish loops we detected in _sequence_. |
| 318 | */ |
| 319 | struct PolishLoopDetector |
| 320 | { |
| 321 | PolishLoopDetector(const QVector<QQuickItem*> &itemsToPolish) |
| 322 | : itemsToPolish(itemsToPolish) |
| 323 | { |
| 324 | } |
| 325 | |
| 326 | /* |
| 327 | * returns true when it detected a likely infinite loop |
| 328 | * (suggests it should abort the polish loop) |
| 329 | **/ |
| 330 | bool check(QQuickItem *item, int itemsRemainingBeforeUpdatePolish) |
| 331 | { |
| 332 | if (itemsToPolish.count() > itemsRemainingBeforeUpdatePolish) { |
| 333 | // Detected potential polish loop. |
| 334 | ++numPolishLoopsInSequence; |
| 335 | if (numPolishLoopsInSequence >= 1000) { |
| 336 | // Start to warn about polish loop after 1000 consecutive polish loops |
| 337 | if (numPolishLoopsInSequence == 100000) { |
| 338 | // We have looped 100,000 times without actually reducing the list of items to |
| 339 | // polish, give up for now. |
| 340 | // This is not a fix, just a remedy so that the application can be somewhat |
| 341 | // responsive. |
| 342 | numPolishLoopsInSequence = 0; |
| 343 | return true; |
| 344 | } else if (numPolishLoopsInSequence < 1005) { |
| 345 | // Show the 5 next items involved in the polish loop. |
| 346 | // (most likely they will be the same 5 items...) |
| 347 | QQuickItem *guiltyItem = itemsToPolish.last(); |
| 348 | qmlWarning(me: item) << "possible QQuickItem::polish() loop" ; |
| 349 | |
| 350 | auto typeAndObjectName = [](QQuickItem *item) { |
| 351 | QString typeName = QQmlMetaType::prettyTypeName(object: item); |
| 352 | QString objName = item->objectName(); |
| 353 | if (!objName.isNull()) |
| 354 | return QLatin1String("%1(%2)" ).arg(args&: typeName, args&: objName); |
| 355 | return typeName; |
| 356 | }; |
| 357 | |
| 358 | qmlWarning(me: guiltyItem) << typeAndObjectName(guiltyItem) |
| 359 | << " called polish() inside updatePolish() of " << typeAndObjectName(item); |
| 360 | |
| 361 | if (numPolishLoopsInSequence == 1004) |
| 362 | // Enough warnings. Reset counter in order to speed things up and re-detect |
| 363 | // more loops |
| 364 | numPolishLoopsInSequence = 0; |
| 365 | } |
| 366 | } |
| 367 | } else { |
| 368 | numPolishLoopsInSequence = 0; |
| 369 | } |
| 370 | return false; |
| 371 | } |
| 372 | const QVector<QQuickItem*> &itemsToPolish; // Just a ref to the one in polishItems() |
| 373 | int numPolishLoopsInSequence = 0; |
| 374 | }; |
| 375 | |
| 376 | void QQuickWindowPrivate::polishItems() |
| 377 | { |
| 378 | // An item can trigger polish on another item, or itself for that matter, |
| 379 | // during its updatePolish() call. Because of this, we cannot simply |
| 380 | // iterate through the set, we must continue pulling items out until it |
| 381 | // is empty. |
| 382 | // In the case where polish is called from updatePolish() either directly |
| 383 | // or indirectly, we use a PolishLoopDetector to determine if a warning should |
| 384 | // be printed to the user. |
| 385 | |
| 386 | PolishLoopDetector polishLoopDetector(itemsToPolish); |
| 387 | while (!itemsToPolish.isEmpty()) { |
| 388 | QQuickItem *item = itemsToPolish.takeLast(); |
| 389 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
| 390 | itemPrivate->polishScheduled = false; |
| 391 | const int itemsRemaining = itemsToPolish.count(); |
| 392 | itemPrivate->updatePolish(); |
| 393 | item->updatePolish(); |
| 394 | if (polishLoopDetector.check(item, itemsRemainingBeforeUpdatePolish: itemsRemaining) == true) |
| 395 | break; |
| 396 | } |
| 397 | |
| 398 | #if QT_CONFIG(im) |
| 399 | if (QQuickItem *focusItem = q_func()->activeFocusItem()) { |
| 400 | // If the current focus item, or any of its anchestors, has changed location |
| 401 | // inside the window, we need inform IM about it. This to ensure that overlays |
| 402 | // such as selection handles will be updated. |
| 403 | const bool isActiveFocusItem = (focusItem == QGuiApplication::focusObject()); |
| 404 | const bool hasImEnabled = focusItem->inputMethodQuery(query: Qt::ImEnabled).toBool(); |
| 405 | if (isActiveFocusItem && hasImEnabled && transformDirtyOnItemOrAncestor(item: focusItem)) |
| 406 | updateFocusItemTransform(); |
| 407 | } |
| 408 | #endif |
| 409 | } |
| 410 | |
| 411 | /*! |
| 412 | * Schedules the window to render another frame. |
| 413 | * |
| 414 | * Calling QQuickWindow::update() differs from QQuickItem::update() in that |
| 415 | * it always triggers a repaint, regardless of changes in the underlying |
| 416 | * scene graph or not. |
| 417 | */ |
| 418 | void QQuickWindow::update() |
| 419 | { |
| 420 | Q_D(QQuickWindow); |
| 421 | if (d->windowManager) |
| 422 | d->windowManager->update(window: this); |
| 423 | else if (d->renderControl) |
| 424 | QQuickRenderControlPrivate::get(renderControl: d->renderControl)->update(); |
| 425 | } |
| 426 | |
| 427 | static void updatePixelRatioHelper(QQuickItem *item, float pixelRatio) |
| 428 | { |
| 429 | if (item->flags() & QQuickItem::ItemHasContents) { |
| 430 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
| 431 | itemPrivate->itemChange(QQuickItem::ItemDevicePixelRatioHasChanged, pixelRatio); |
| 432 | } |
| 433 | |
| 434 | QList <QQuickItem *> items = item->childItems(); |
| 435 | for (int i = 0; i < items.size(); ++i) |
| 436 | updatePixelRatioHelper(item: items.at(i), pixelRatio); |
| 437 | } |
| 438 | |
| 439 | void QQuickWindow::physicalDpiChanged() |
| 440 | { |
| 441 | Q_D(QQuickWindow); |
| 442 | const qreal newPixelRatio = screen()->devicePixelRatio(); |
| 443 | if (qFuzzyCompare(p1: newPixelRatio, p2: d->devicePixelRatio)) |
| 444 | return; |
| 445 | d->devicePixelRatio = newPixelRatio; |
| 446 | if (d->contentItem) |
| 447 | updatePixelRatioHelper(item: d->contentItem, pixelRatio: newPixelRatio); |
| 448 | } |
| 449 | |
| 450 | void QQuickWindow::handleScreenChanged(QScreen *screen) |
| 451 | { |
| 452 | Q_D(QQuickWindow); |
| 453 | // we connected to the initial screen in QQuickWindowPrivate::init, but the screen changed |
| 454 | disconnect(d->physicalDpiChangedConnection); |
| 455 | if (screen) { |
| 456 | physicalDpiChanged(); |
| 457 | // When physical DPI changes on the same screen, either the resolution or the device pixel |
| 458 | // ratio changed. We must check what it is. Device pixel ratio does not have its own |
| 459 | // ...Changed() signal. Reconnect, same as in QQuickWindowPrivate::init. |
| 460 | d->physicalDpiChangedConnection = connect(sender: screen, signal: &QScreen::physicalDotsPerInchChanged, |
| 461 | receiver: this, slot: &QQuickWindow::physicalDpiChanged); |
| 462 | } |
| 463 | |
| 464 | d->forcePolish(); |
| 465 | } |
| 466 | |
| 467 | void forcePolishHelper(QQuickItem *item) |
| 468 | { |
| 469 | if (item->flags() & QQuickItem::ItemHasContents) { |
| 470 | item->polish(); |
| 471 | } |
| 472 | |
| 473 | QList <QQuickItem *> items = item->childItems(); |
| 474 | for (int i=0; i<items.size(); ++i) |
| 475 | forcePolishHelper(item: items.at(i)); |
| 476 | } |
| 477 | |
| 478 | /*! |
| 479 | Schedules polish events on all items in the scene. |
| 480 | */ |
| 481 | void QQuickWindowPrivate::forcePolish() |
| 482 | { |
| 483 | Q_Q(QQuickWindow); |
| 484 | if (!q->screen()) |
| 485 | return; |
| 486 | forcePolishHelper(item: contentItem); |
| 487 | } |
| 488 | |
| 489 | void forceUpdate(QQuickItem *item) |
| 490 | { |
| 491 | if (item->flags() & QQuickItem::ItemHasContents) |
| 492 | item->update(); |
| 493 | QQuickItemPrivate::get(item)->dirty(QQuickItemPrivate::ChildrenUpdateMask); |
| 494 | |
| 495 | QList <QQuickItem *> items = item->childItems(); |
| 496 | for (int i=0; i<items.size(); ++i) |
| 497 | forceUpdate(item: items.at(i)); |
| 498 | } |
| 499 | |
| 500 | void QQuickWindowPrivate::syncSceneGraph() |
| 501 | { |
| 502 | Q_Q(QQuickWindow); |
| 503 | |
| 504 | // Calculate the dpr the same way renderSceneGraph() will. |
| 505 | qreal devicePixelRatio = q->effectiveDevicePixelRatio(); |
| 506 | if (renderTargetId && !QQuickRenderControl::renderWindowFor(win: q)) |
| 507 | devicePixelRatio = 1; |
| 508 | |
| 509 | context->prepareSync(devicePixelRatio, cb: rhi ? swapchain->currentFrameCommandBuffer() : nullptr); |
| 510 | |
| 511 | animationController->beforeNodeSync(); |
| 512 | |
| 513 | emit q->beforeSynchronizing(); |
| 514 | runAndClearJobs(jobs: &beforeSynchronizingJobs); |
| 515 | if (!renderer) { |
| 516 | forceUpdate(item: contentItem); |
| 517 | |
| 518 | QSGRootNode *rootNode = new QSGRootNode; |
| 519 | rootNode->appendChildNode(node: QQuickItemPrivate::get(item: contentItem)->itemNode()); |
| 520 | renderer = context->createRenderer(); |
| 521 | renderer->setRootNode(rootNode); |
| 522 | } |
| 523 | |
| 524 | updateDirtyNodes(); |
| 525 | |
| 526 | animationController->afterNodeSync(); |
| 527 | |
| 528 | // Copy the current state of clearing from window into renderer. |
| 529 | renderer->setClearColor(clearColor); |
| 530 | QSGAbstractRenderer::ClearMode mode = QSGAbstractRenderer::ClearStencilBuffer | QSGAbstractRenderer::ClearDepthBuffer; |
| 531 | if (clearBeforeRendering) |
| 532 | mode |= QSGAbstractRenderer::ClearColorBuffer; |
| 533 | renderer->setClearMode(mode); |
| 534 | |
| 535 | renderer->setCustomRenderMode(customRenderMode); |
| 536 | |
| 537 | emit q->afterSynchronizing(); |
| 538 | runAndClearJobs(jobs: &afterSynchronizingJobs); |
| 539 | } |
| 540 | |
| 541 | void QQuickWindowPrivate::emitBeforeRenderPassRecording(void *ud) |
| 542 | { |
| 543 | QQuickWindow *w = reinterpret_cast<QQuickWindow *>(ud); |
| 544 | emit w->beforeRenderPassRecording(); |
| 545 | } |
| 546 | |
| 547 | void QQuickWindowPrivate::emitAfterRenderPassRecording(void *ud) |
| 548 | { |
| 549 | QQuickWindow *w = reinterpret_cast<QQuickWindow *>(ud); |
| 550 | emit w->afterRenderPassRecording(); |
| 551 | } |
| 552 | |
| 553 | void QQuickWindowPrivate::renderSceneGraph(const QSize &size, const QSize &surfaceSize) |
| 554 | { |
| 555 | Q_Q(QQuickWindow); |
| 556 | if (!renderer) |
| 557 | return; |
| 558 | |
| 559 | if (rhi) { |
| 560 | // ### no offscreen ("renderTargetId") support yet |
| 561 | context->beginNextRhiFrame(renderer, |
| 562 | rt: swapchain->currentFrameRenderTarget(), |
| 563 | rp: rpDescForSwapchain, |
| 564 | cb: swapchain->currentFrameCommandBuffer(), |
| 565 | mainPassRecordingStart: emitBeforeRenderPassRecording, |
| 566 | mainPassRecordingEnd: emitAfterRenderPassRecording, |
| 567 | callbackUserData: q); |
| 568 | } else { |
| 569 | context->beginNextFrame(renderer, |
| 570 | mainPassRecordingStart: emitBeforeRenderPassRecording, |
| 571 | mainPassRecordingEnd: emitAfterRenderPassRecording, |
| 572 | callbackUserData: q); |
| 573 | } |
| 574 | |
| 575 | animationController->advance(); |
| 576 | emit q->beforeRendering(); |
| 577 | runAndClearJobs(jobs: &beforeRenderingJobs); |
| 578 | if (!customRenderStage || !customRenderStage->render()) { |
| 579 | int fboId = 0; |
| 580 | const qreal devicePixelRatio = q->effectiveDevicePixelRatio(); |
| 581 | if (renderTargetId) { |
| 582 | QRect rect(QPoint(0, 0), renderTargetSize); |
| 583 | fboId = renderTargetId; |
| 584 | renderer->setDeviceRect(rect); |
| 585 | renderer->setViewportRect(rect); |
| 586 | if (QQuickRenderControl::renderWindowFor(win: q)) { |
| 587 | renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), size)); |
| 588 | renderer->setDevicePixelRatio(devicePixelRatio); |
| 589 | } else { |
| 590 | renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), rect.size())); |
| 591 | renderer->setDevicePixelRatio(1); |
| 592 | } |
| 593 | } else { |
| 594 | QSize pixelSize; |
| 595 | QSizeF logicalSize; |
| 596 | if (surfaceSize.isEmpty()) { |
| 597 | pixelSize = size * devicePixelRatio; |
| 598 | logicalSize = size; |
| 599 | } else { |
| 600 | pixelSize = surfaceSize; |
| 601 | logicalSize = QSizeF(surfaceSize) / devicePixelRatio; |
| 602 | } |
| 603 | QRect rect(QPoint(0, 0), pixelSize); |
| 604 | renderer->setDeviceRect(rect); |
| 605 | renderer->setViewportRect(rect); |
| 606 | const bool flipY = rhi ? !rhi->isYUpInNDC() : false; |
| 607 | QSGAbstractRenderer::MatrixTransformFlags matrixFlags; |
| 608 | if (flipY) |
| 609 | matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY; |
| 610 | renderer->setProjectionMatrixToRect(rect: QRectF(QPoint(0, 0), logicalSize), flags: matrixFlags); |
| 611 | renderer->setDevicePixelRatio(devicePixelRatio); |
| 612 | } |
| 613 | |
| 614 | if (rhi) |
| 615 | context->renderNextRhiFrame(renderer); |
| 616 | else |
| 617 | context->renderNextFrame(renderer, fboId); |
| 618 | } |
| 619 | emit q->afterRendering(); |
| 620 | runAndClearJobs(jobs: &afterRenderingJobs); |
| 621 | |
| 622 | if (rhi) |
| 623 | context->endNextRhiFrame(renderer); |
| 624 | else |
| 625 | context->endNextFrame(renderer); |
| 626 | |
| 627 | if (renderer && renderer->hasCustomRenderModeWithContinuousUpdate()) { |
| 628 | // For the overdraw visualizer. This update is not urgent so avoid a |
| 629 | // direct update() call, this is only here to keep the overdraw |
| 630 | // visualization box rotating even when the scene is static. |
| 631 | QCoreApplication::postEvent(receiver: q, event: new QEvent(QEvent::Type(FullUpdateRequest))); |
| 632 | } |
| 633 | } |
| 634 | |
| 635 | QQuickWindowPrivate::QQuickWindowPrivate() |
| 636 | : contentItem(nullptr) |
| 637 | , activeFocusItem(nullptr) |
| 638 | #if QT_CONFIG(cursor) |
| 639 | , cursorItem(nullptr) |
| 640 | , cursorHandler(nullptr) |
| 641 | #endif |
| 642 | #if QT_CONFIG(quick_draganddrop) |
| 643 | , dragGrabber(nullptr) |
| 644 | #endif |
| 645 | , touchMouseId(-1) |
| 646 | , touchMouseDevice(nullptr) |
| 647 | , touchMousePressTimestamp(0) |
| 648 | , dirtyItemList(nullptr) |
| 649 | , devicePixelRatio(0) |
| 650 | , context(nullptr) |
| 651 | , renderer(nullptr) |
| 652 | , windowManager(nullptr) |
| 653 | , renderControl(nullptr) |
| 654 | , pointerEventRecursionGuard(0) |
| 655 | , customRenderStage(nullptr) |
| 656 | , clearColor(Qt::white) |
| 657 | , clearBeforeRendering(true) |
| 658 | , persistentGLContext(true) |
| 659 | , persistentSceneGraph(true) |
| 660 | , lastWheelEventAccepted(false) |
| 661 | , componentCompleted(true) |
| 662 | , allowChildEventFiltering(true) |
| 663 | , allowDoubleClick(true) |
| 664 | , lastFocusReason(Qt::OtherFocusReason) |
| 665 | , renderTarget(nullptr) |
| 666 | , renderTargetId(0) |
| 667 | , vaoHelper(nullptr) |
| 668 | , incubationController(nullptr) |
| 669 | , hasActiveSwapchain(false) |
| 670 | , hasRenderableSwapchain(false) |
| 671 | , swapchainJustBecameRenderable(false) |
| 672 | { |
| 673 | #if QT_CONFIG(quick_draganddrop) |
| 674 | dragGrabber = new QQuickDragGrabber; |
| 675 | #endif |
| 676 | } |
| 677 | |
| 678 | QQuickWindowPrivate::~QQuickWindowPrivate() |
| 679 | { |
| 680 | delete customRenderStage; |
| 681 | if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>()) |
| 682 | service->removeWindow(q_func()); |
| 683 | } |
| 684 | |
| 685 | void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control) |
| 686 | { |
| 687 | q_ptr = c; |
| 688 | |
| 689 | |
| 690 | Q_Q(QQuickWindow); |
| 691 | |
| 692 | contentItem = new QQuickRootItem; |
| 693 | QQml_setParent_noEvent(object: contentItem, parent: c); |
| 694 | QQmlEngine::setObjectOwnership(contentItem, QQmlEngine::CppOwnership); |
| 695 | QQuickItemPrivate *contentItemPrivate = QQuickItemPrivate::get(item: contentItem); |
| 696 | contentItemPrivate->window = q; |
| 697 | contentItemPrivate->windowRefCount = 1; |
| 698 | contentItemPrivate->flags |= QQuickItem::ItemIsFocusScope; |
| 699 | contentItem->setSize(q->size()); |
| 700 | |
| 701 | customRenderMode = qgetenv(varName: "QSG_VISUALIZE" ); |
| 702 | renderControl = control; |
| 703 | if (renderControl) |
| 704 | QQuickRenderControlPrivate::get(renderControl)->window = q; |
| 705 | |
| 706 | if (!renderControl) |
| 707 | windowManager = QSGRenderLoop::instance(); |
| 708 | |
| 709 | Q_ASSERT(windowManager || renderControl); |
| 710 | |
| 711 | if (QScreen *screen = q->screen()) { |
| 712 | devicePixelRatio = screen->devicePixelRatio(); |
| 713 | // if the screen changes, then QQuickWindow::handleScreenChanged disconnects |
| 714 | // and connects to the new screen |
| 715 | physicalDpiChangedConnection = QObject::connect(sender: screen, signal: &QScreen::physicalDotsPerInchChanged, |
| 716 | receiver: q, slot: &QQuickWindow::physicalDpiChanged); |
| 717 | } |
| 718 | |
| 719 | QSGContext *sg; |
| 720 | if (renderControl) { |
| 721 | QQuickRenderControlPrivate *renderControlPriv = QQuickRenderControlPrivate::get(renderControl); |
| 722 | sg = renderControlPriv->sg; |
| 723 | context = renderControlPriv->rc; |
| 724 | } else { |
| 725 | windowManager->addWindow(win: q); |
| 726 | sg = windowManager->sceneGraphContext(); |
| 727 | context = windowManager->createRenderContext(sg); |
| 728 | } |
| 729 | |
| 730 | q->setSurfaceType(windowManager ? windowManager->windowSurfaceType() : QSurface::OpenGLSurface); |
| 731 | q->setFormat(sg->defaultSurfaceFormat()); |
| 732 | #if QT_CONFIG(vulkan) |
| 733 | if (q->surfaceType() == QSurface::VulkanSurface) |
| 734 | q->setVulkanInstance(QSGRhiSupport::vulkanInstance()); |
| 735 | #endif |
| 736 | |
| 737 | animationController.reset(other: new QQuickAnimatorController(q)); |
| 738 | |
| 739 | QObject::connect(sender: context, SIGNAL(initialized()), receiver: q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection); |
| 740 | QObject::connect(sender: context, SIGNAL(invalidated()), receiver: q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection); |
| 741 | QObject::connect(sender: context, SIGNAL(invalidated()), receiver: q, SLOT(cleanupSceneGraph()), Qt::DirectConnection); |
| 742 | |
| 743 | QObject::connect(sender: q, SIGNAL(focusObjectChanged(QObject*)), receiver: q, SIGNAL(activeFocusItemChanged())); |
| 744 | QObject::connect(sender: q, SIGNAL(screenChanged(QScreen*)), receiver: q, SLOT(handleScreenChanged(QScreen*))); |
| 745 | QObject::connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)), |
| 746 | receiver: q, SLOT(handleApplicationStateChanged(Qt::ApplicationState))); |
| 747 | QObject::connect(sender: q, SIGNAL(frameSwapped()), receiver: q, SLOT(runJobsAfterSwap()), Qt::DirectConnection); |
| 748 | |
| 749 | if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>()) |
| 750 | service->addWindow(q); |
| 751 | } |
| 752 | |
| 753 | void QQuickWindow::handleApplicationStateChanged(Qt::ApplicationState state) |
| 754 | { |
| 755 | Q_D(QQuickWindow); |
| 756 | if (state != Qt::ApplicationActive && d->contentItem) |
| 757 | d->contentItem->windowDeactivateEvent(); |
| 758 | } |
| 759 | |
| 760 | /*! |
| 761 | \property QQuickWindow::data |
| 762 | \internal |
| 763 | */ |
| 764 | |
| 765 | QQmlListProperty<QObject> QQuickWindowPrivate::data() |
| 766 | { |
| 767 | return QQmlListProperty<QObject>(q_func(), nullptr, |
| 768 | QQuickWindowPrivate::data_append, |
| 769 | QQuickWindowPrivate::data_count, |
| 770 | QQuickWindowPrivate::data_at, |
| 771 | QQuickWindowPrivate::data_clear, |
| 772 | QQuickWindowPrivate::data_replace, |
| 773 | QQuickWindowPrivate::data_removeLast); |
| 774 | } |
| 775 | |
| 776 | static QMouseEvent *touchToMouseEvent(QEvent::Type type, const QTouchEvent::TouchPoint &p, QTouchEvent *event, QQuickItem *item, bool transformNeeded = true) |
| 777 | { |
| 778 | Q_ASSERT(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents)); |
| 779 | // The touch point local position and velocity are not yet transformed. |
| 780 | QMouseEvent *me = new QMouseEvent(type, transformNeeded ? item->mapFromScene(point: p.scenePos()) : p.pos(), p.scenePos(), p.screenPos(), |
| 781 | Qt::LeftButton, (type == QEvent::MouseButtonRelease ? Qt::NoButton : Qt::LeftButton), event->modifiers()); |
| 782 | me->setAccepted(true); |
| 783 | me->setTimestamp(event->timestamp()); |
| 784 | QVector2D transformedVelocity = p.velocity(); |
| 785 | if (transformNeeded) { |
| 786 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
| 787 | QMatrix4x4 transformMatrix(itemPrivate->windowToItemTransform()); |
| 788 | transformedVelocity = transformMatrix.mapVector(vector: p.velocity()).toVector2D(); |
| 789 | } |
| 790 | QGuiApplicationPrivate::setMouseEventCapsAndVelocity(event: me, caps: event->device()->capabilities(), velocity: transformedVelocity); |
| 791 | QGuiApplicationPrivate::setMouseEventSource(event: me, source: Qt::MouseEventSynthesizedByQt); |
| 792 | return me; |
| 793 | } |
| 794 | |
| 795 | bool QQuickWindowPrivate::checkIfDoubleTapped(ulong newPressEventTimestamp, QPoint newPressPos) |
| 796 | { |
| 797 | bool doubleClicked = false; |
| 798 | |
| 799 | if (touchMousePressTimestamp > 0) { |
| 800 | QPoint distanceBetweenPresses = newPressPos - touchMousePressPos; |
| 801 | const int doubleTapDistance = QGuiApplication::styleHints()->touchDoubleTapDistance(); |
| 802 | doubleClicked = (qAbs(t: distanceBetweenPresses.x()) <= doubleTapDistance) && (qAbs(t: distanceBetweenPresses.y()) <= doubleTapDistance); |
| 803 | |
| 804 | if (doubleClicked) { |
| 805 | ulong timeBetweenPresses = newPressEventTimestamp - touchMousePressTimestamp; |
| 806 | ulong doubleClickInterval = static_cast<ulong>(QGuiApplication::styleHints()-> |
| 807 | mouseDoubleClickInterval()); |
| 808 | doubleClicked = timeBetweenPresses < doubleClickInterval; |
| 809 | } |
| 810 | } |
| 811 | if (doubleClicked) { |
| 812 | touchMousePressTimestamp = 0; |
| 813 | } else { |
| 814 | touchMousePressTimestamp = newPressEventTimestamp; |
| 815 | touchMousePressPos = newPressPos; |
| 816 | } |
| 817 | |
| 818 | return doubleClicked; |
| 819 | } |
| 820 | |
| 821 | void QQuickWindowPrivate::cancelTouchMouseSynthesis() |
| 822 | { |
| 823 | qCDebug(DBG_TOUCH_TARGET); |
| 824 | touchMouseId = -1; |
| 825 | touchMouseDevice = nullptr; |
| 826 | } |
| 827 | |
| 828 | bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEvent *pointerEvent) |
| 829 | { |
| 830 | Q_ASSERT(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents)); |
| 831 | Q_Q(QQuickWindow); |
| 832 | auto device = pointerEvent->device(); |
| 833 | |
| 834 | // A touch event from a trackpad is likely to be followed by a mouse or gesture event, so mouse event synth is redundant |
| 835 | if (device->type() == QQuickPointerDevice::TouchPad && device->capabilities().testFlag(flag: QQuickPointerDevice::MouseEmulation)) { |
| 836 | qCDebug(DBG_TOUCH_TARGET) << "skipping delivery of synth-mouse event from" << device; |
| 837 | return false; |
| 838 | } |
| 839 | |
| 840 | // FIXME: make this work for mouse events too and get rid of the asTouchEvent in here. |
| 841 | Q_ASSERT(pointerEvent->asPointerTouchEvent()); |
| 842 | QScopedPointer<QTouchEvent> event(pointerEvent->asPointerTouchEvent()->touchEventForItem(item)); |
| 843 | if (event.isNull()) |
| 844 | return false; |
| 845 | |
| 846 | // For each point, check if it is accepted, if not, try the next point. |
| 847 | // Any of the fingers can become the mouse one. |
| 848 | // This can happen because a mouse area might not accept an event at some point but another. |
| 849 | for (int i = 0; i < event->touchPoints().count(); ++i) { |
| 850 | const QTouchEvent::TouchPoint &p = event->touchPoints().at(i); |
| 851 | // A new touch point |
| 852 | if (touchMouseId == -1 && p.state() & Qt::TouchPointPressed) { |
| 853 | QPointF pos = item->mapFromScene(point: p.scenePos()); |
| 854 | |
| 855 | // probably redundant, we check bounds in the calling function (matchingNewPoints) |
| 856 | if (!item->contains(point: pos)) |
| 857 | break; |
| 858 | |
| 859 | qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << Qt::hex << p.id() << "->" << item; |
| 860 | QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(type: QEvent::MouseButtonPress, p, event: event.data(), item, transformNeeded: false)); |
| 861 | |
| 862 | // Send a single press and see if that's accepted |
| 863 | QCoreApplication::sendEvent(receiver: item, event: mousePress.data()); |
| 864 | event->setAccepted(mousePress->isAccepted()); |
| 865 | if (mousePress->isAccepted()) { |
| 866 | touchMouseDevice = device; |
| 867 | touchMouseId = p.id(); |
| 868 | if (!q->mouseGrabberItem()) |
| 869 | item->grabMouse(); |
| 870 | if (auto pointerEventPoint = pointerEvent->pointById(pointId: p.id())) |
| 871 | pointerEventPoint->setGrabberItem(item); |
| 872 | |
| 873 | if (checkIfDoubleTapped(newPressEventTimestamp: event->timestamp(), newPressPos: p.screenPos().toPoint())) { |
| 874 | // since we synth the mouse event from from touch, we respect the |
| 875 | // QPlatformTheme::TouchDoubleTapDistance instead of QPlatformTheme::MouseDoubleClickDistance |
| 876 | QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(type: QEvent::MouseButtonDblClick, p, event: event.data(), item, transformNeeded: false)); |
| 877 | QCoreApplication::sendEvent(receiver: item, event: mouseDoubleClick.data()); |
| 878 | event->setAccepted(mouseDoubleClick->isAccepted()); |
| 879 | if (!mouseDoubleClick->isAccepted()) |
| 880 | cancelTouchMouseSynthesis(); |
| 881 | } |
| 882 | |
| 883 | return true; |
| 884 | } |
| 885 | // try the next point |
| 886 | |
| 887 | // Touch point was there before and moved |
| 888 | } else if (touchMouseDevice == device && p.id() == touchMouseId) { |
| 889 | if (p.state() & Qt::TouchPointMoved) { |
| 890 | if (touchMousePressTimestamp != 0) { |
| 891 | const int doubleTapDistance = QGuiApplicationPrivate::platformTheme()->themeHint(hint: QPlatformTheme::TouchDoubleTapDistance).toInt(); |
| 892 | const QPoint moveDelta = p.screenPos().toPoint() - touchMousePressPos; |
| 893 | if (moveDelta.x() >= doubleTapDistance || moveDelta.y() >= doubleTapDistance) |
| 894 | touchMousePressTimestamp = 0; // Got dragged too far, dismiss the double tap |
| 895 | } |
| 896 | if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) { |
| 897 | QScopedPointer<QMouseEvent> me(touchToMouseEvent(type: QEvent::MouseMove, p, event: event.data(), item: mouseGrabberItem, transformNeeded: false)); |
| 898 | QCoreApplication::sendEvent(receiver: item, event: me.data()); |
| 899 | event->setAccepted(me->isAccepted()); |
| 900 | if (me->isAccepted()) { |
| 901 | qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << Qt::hex << p.id() << "->" << mouseGrabberItem; |
| 902 | } |
| 903 | return event->isAccepted(); |
| 904 | } else { |
| 905 | // no grabber, check if we care about mouse hover |
| 906 | // FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now. |
| 907 | // hover for touch??? |
| 908 | QScopedPointer<QMouseEvent> me(touchToMouseEvent(type: QEvent::MouseMove, p, event: event.data(), item, transformNeeded: false)); |
| 909 | if (lastMousePosition.isNull()) |
| 910 | lastMousePosition = me->windowPos(); |
| 911 | QPointF last = lastMousePosition; |
| 912 | lastMousePosition = me->windowPos(); |
| 913 | |
| 914 | bool accepted = me->isAccepted(); |
| 915 | bool delivered = deliverHoverEvent(contentItem, scenePos: me->windowPos(), lastScenePos: last, modifiers: me->modifiers(), timestamp: me->timestamp(), accepted); |
| 916 | if (!delivered) { |
| 917 | //take care of any exits |
| 918 | accepted = clearHover(timestamp: me->timestamp()); |
| 919 | } |
| 920 | me->setAccepted(accepted); |
| 921 | break; |
| 922 | } |
| 923 | } else if (p.state() & Qt::TouchPointReleased) { |
| 924 | // currently handled point was released |
| 925 | if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) { |
| 926 | QScopedPointer<QMouseEvent> me(touchToMouseEvent(type: QEvent::MouseButtonRelease, p, event: event.data(), item: mouseGrabberItem, transformNeeded: false)); |
| 927 | QCoreApplication::sendEvent(receiver: item, event: me.data()); |
| 928 | |
| 929 | if (item->acceptHoverEvents() && p.screenPos() != QGuiApplicationPrivate::lastCursorPosition) { |
| 930 | QPointF localMousePos(qInf(), qInf()); |
| 931 | if (QWindow *w = item->window()) |
| 932 | localMousePos = item->mapFromScene(point: w->mapFromGlobal(pos: QGuiApplicationPrivate::lastCursorPosition.toPoint())); |
| 933 | QMouseEvent mm(QEvent::MouseMove, localMousePos, QGuiApplicationPrivate::lastCursorPosition, |
| 934 | Qt::NoButton, Qt::NoButton, event->modifiers()); |
| 935 | QCoreApplication::sendEvent(receiver: item, event: &mm); |
| 936 | } |
| 937 | if (q->mouseGrabberItem()) // might have ungrabbed due to event |
| 938 | q->mouseGrabberItem()->ungrabMouse(); |
| 939 | |
| 940 | cancelTouchMouseSynthesis(); |
| 941 | return me->isAccepted(); |
| 942 | } |
| 943 | } |
| 944 | break; |
| 945 | } |
| 946 | } |
| 947 | return false; |
| 948 | } |
| 949 | |
| 950 | void QQuickWindowPrivate::grabTouchPoints(QObject *grabber, const QVector<int> &ids) |
| 951 | { |
| 952 | QQuickPointerEvent *ev = nullptr; |
| 953 | for (int i = 0; i < ids.count(); ++i) { |
| 954 | int id = ids.at(i); |
| 955 | if (Q_UNLIKELY(id < 0)) { |
| 956 | qWarning(msg: "ignoring grab of touchpoint %d" , id); |
| 957 | continue; |
| 958 | } |
| 959 | if (id == touchMouseId) { |
| 960 | auto point = pointerEventInstance(device: touchMouseDevice)->pointById(pointId: id); |
| 961 | auto touchMouseGrabber = point->grabberItem(); |
| 962 | if (touchMouseGrabber) { |
| 963 | point->setExclusiveGrabber(nullptr); |
| 964 | touchMouseGrabber->mouseUngrabEvent(); |
| 965 | touchMouseGrabber->touchUngrabEvent(); |
| 966 | cancelTouchMouseSynthesis(); |
| 967 | } |
| 968 | qCDebug(DBG_MOUSE_TARGET) << "grabTouchPoints: mouse grabber changed due to grabTouchPoints:" << touchMouseGrabber << "-> null" ; |
| 969 | } |
| 970 | |
| 971 | // optimization to avoid the loop over devices below: |
| 972 | // all ids are probably from the same event, so we don't have to search |
| 973 | if (ev) { |
| 974 | auto point = ev->pointById(pointId: id); |
| 975 | if (point && point->exclusiveGrabber() != grabber) { |
| 976 | point->setExclusiveGrabber(grabber); |
| 977 | continue; // next id in the ids loop |
| 978 | } |
| 979 | } |
| 980 | // search all devices for a QQuickPointerEvent instance that is delivering the point with id |
| 981 | const auto touchDevices = QQuickPointerDevice::touchDevices(); |
| 982 | for (auto device : touchDevices) { |
| 983 | QQuickPointerEvent *pev = pointerEventInstance(device); |
| 984 | auto point = pev->pointById(pointId: id); |
| 985 | if (point) { |
| 986 | ev = pev; |
| 987 | if (point->exclusiveGrabber() != grabber) |
| 988 | point->setExclusiveGrabber(grabber); |
| 989 | break; // out of touchDevices loop |
| 990 | } |
| 991 | } |
| 992 | } |
| 993 | } |
| 994 | |
| 995 | /*! |
| 996 | Ungrabs all touchpoint grabs and/or the mouse grab from the given item \a grabber. |
| 997 | This should not be called when processing a release event - that's redundant. |
| 998 | It is called in other cases, when the points may not be released, but the item |
| 999 | nevertheless must lose its grab due to becoming disabled, invisible, etc. |
| 1000 | QQuickEventPoint::setGrabberItem() calls touchUngrabEvent() when all points are released, |
| 1001 | but if not all points are released, it cannot be sure whether to call touchUngrabEvent() |
| 1002 | or not; so we have to do it here. |
| 1003 | */ |
| 1004 | void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch) |
| 1005 | { |
| 1006 | Q_Q(QQuickWindow); |
| 1007 | if (Q_LIKELY(mouse) && q->mouseGrabberItem() == grabber) { |
| 1008 | bool fromTouch = isDeliveringTouchAsMouse(); |
| 1009 | auto point = fromTouch ? |
| 1010 | pointerEventInstance(device: touchMouseDevice)->pointById(pointId: touchMouseId) : |
| 1011 | pointerEventInstance(device: QQuickPointerDevice::genericMouseDevice())->point(i: 0); |
| 1012 | QQuickItem *oldGrabber = point->grabberItem(); |
| 1013 | qCDebug(DBG_MOUSE_TARGET) << "removeGrabber" << oldGrabber << "-> null" ; |
| 1014 | point->setGrabberItem(nullptr); |
| 1015 | sendUngrabEvent(grabber: oldGrabber, touch: fromTouch); |
| 1016 | } |
| 1017 | if (Q_LIKELY(touch)) { |
| 1018 | bool ungrab = false; |
| 1019 | const auto touchDevices = QQuickPointerDevice::touchDevices(); |
| 1020 | for (auto device : touchDevices) { |
| 1021 | if (auto pointerEvent = queryPointerEventInstance(device)) { |
| 1022 | for (int i = 0; i < pointerEvent->pointCount(); ++i) { |
| 1023 | if (pointerEvent->point(i)->exclusiveGrabber() == grabber) { |
| 1024 | pointerEvent->point(i)->setGrabberItem(nullptr); |
| 1025 | ungrab = true; |
| 1026 | } |
| 1027 | } |
| 1028 | } |
| 1029 | } |
| 1030 | if (ungrab) |
| 1031 | grabber->touchUngrabEvent(); |
| 1032 | } |
| 1033 | } |
| 1034 | |
| 1035 | void QQuickWindowPrivate::sendUngrabEvent(QQuickItem *grabber, bool touch) |
| 1036 | { |
| 1037 | if (!grabber) |
| 1038 | return; |
| 1039 | QEvent e(QEvent::UngrabMouse); |
| 1040 | hasFiltered.clear(); |
| 1041 | if (!sendFilteredMouseEvent(event: &e, receiver: grabber, filteringParent: grabber->parentItem())) { |
| 1042 | grabber->mouseUngrabEvent(); |
| 1043 | if (touch) |
| 1044 | grabber->touchUngrabEvent(); |
| 1045 | } |
| 1046 | } |
| 1047 | |
| 1048 | /*! |
| 1049 | Translates the data in \a touchEvent to this window. This method leaves the item local positions in |
| 1050 | \a touchEvent untouched (these are filled in later). |
| 1051 | */ |
| 1052 | void QQuickWindowPrivate::translateTouchEvent(QTouchEvent *touchEvent) |
| 1053 | { |
| 1054 | QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints(); |
| 1055 | for (int i = 0; i < touchPoints.count(); ++i) { |
| 1056 | QTouchEvent::TouchPoint &touchPoint = touchPoints[i]; |
| 1057 | touchPoint.setScenePos(touchPoint.pos()); |
| 1058 | touchPoint.setStartScenePos(touchPoint.startPos()); |
| 1059 | touchPoint.setLastScenePos(touchPoint.lastPos()); |
| 1060 | } |
| 1061 | touchEvent->setTouchPoints(touchPoints); |
| 1062 | } |
| 1063 | |
| 1064 | |
| 1065 | static inline bool windowHasFocus(QQuickWindow *win) |
| 1066 | { |
| 1067 | const QWindow *focusWindow = QGuiApplication::focusWindow(); |
| 1068 | return win == focusWindow || QQuickRenderControl::renderWindowFor(win) == focusWindow; |
| 1069 | } |
| 1070 | |
| 1071 | #ifdef Q_OS_WEBOS |
| 1072 | // Temporary fix for webOS until multi-seat is implemented see QTBUG-85272 |
| 1073 | static inline bool singleWindowOnScreen(QQuickWindow *win) |
| 1074 | { |
| 1075 | const QWindowList windowList = QGuiApplication::allWindows(); |
| 1076 | for (int i = 0; i < windowList.count(); i++) { |
| 1077 | QWindow *ii = windowList.at(i); |
| 1078 | if (ii == win) |
| 1079 | continue; |
| 1080 | if (ii->screen() == win->screen()) |
| 1081 | return false; |
| 1082 | } |
| 1083 | |
| 1084 | return true; |
| 1085 | } |
| 1086 | #endif |
| 1087 | |
| 1088 | /*! |
| 1089 | Set the focus inside \a scope to be \a item. |
| 1090 | If the scope contains the active focus item, it will be changed to \a item. |
| 1091 | Calls notifyFocusChangesRecur for all changed items. |
| 1092 | */ |
| 1093 | void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions options) |
| 1094 | { |
| 1095 | Q_Q(QQuickWindow); |
| 1096 | |
| 1097 | Q_ASSERT(item); |
| 1098 | Q_ASSERT(scope || item == contentItem); |
| 1099 | |
| 1100 | qCDebug(DBG_FOCUS) << "QQuickWindowPrivate::setFocusInScope():" ; |
| 1101 | qCDebug(DBG_FOCUS) << " scope:" << (QObject *)scope; |
| 1102 | if (scope) |
| 1103 | qCDebug(DBG_FOCUS) << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(item: scope)->subFocusItem; |
| 1104 | qCDebug(DBG_FOCUS) << " item:" << (QObject *)item; |
| 1105 | qCDebug(DBG_FOCUS) << " activeFocusItem:" << (QObject *)activeFocusItem; |
| 1106 | |
| 1107 | QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(item: scope) : nullptr; |
| 1108 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
| 1109 | |
| 1110 | QQuickItem *oldActiveFocusItem = nullptr; |
| 1111 | QQuickItem *currentActiveFocusItem = activeFocusItem; |
| 1112 | QQuickItem *newActiveFocusItem = nullptr; |
| 1113 | bool sendFocusIn = false; |
| 1114 | |
| 1115 | lastFocusReason = reason; |
| 1116 | |
| 1117 | QVarLengthArray<QQuickItem *, 20> changed; |
| 1118 | |
| 1119 | // Does this change the active focus? |
| 1120 | if (item == contentItem || scopePrivate->activeFocus) { |
| 1121 | oldActiveFocusItem = activeFocusItem; |
| 1122 | if (item->isEnabled()) { |
| 1123 | newActiveFocusItem = item; |
| 1124 | while (newActiveFocusItem->isFocusScope() |
| 1125 | && newActiveFocusItem->scopedFocusItem() |
| 1126 | && newActiveFocusItem->scopedFocusItem()->isEnabled()) { |
| 1127 | newActiveFocusItem = newActiveFocusItem->scopedFocusItem(); |
| 1128 | } |
| 1129 | } else { |
| 1130 | newActiveFocusItem = scope; |
| 1131 | } |
| 1132 | |
| 1133 | if (oldActiveFocusItem) { |
| 1134 | #if QT_CONFIG(im) |
| 1135 | QGuiApplication::inputMethod()->commit(); |
| 1136 | #endif |
| 1137 | |
| 1138 | activeFocusItem = nullptr; |
| 1139 | |
| 1140 | QQuickItem *afi = oldActiveFocusItem; |
| 1141 | while (afi && afi != scope) { |
| 1142 | if (QQuickItemPrivate::get(item: afi)->activeFocus) { |
| 1143 | QQuickItemPrivate::get(item: afi)->activeFocus = false; |
| 1144 | changed << afi; |
| 1145 | } |
| 1146 | afi = afi->parentItem(); |
| 1147 | } |
| 1148 | } |
| 1149 | } |
| 1150 | |
| 1151 | if (item != contentItem && !(options & DontChangeSubFocusItem)) { |
| 1152 | QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem; |
| 1153 | if (oldSubFocusItem) { |
| 1154 | QQuickItemPrivate::get(item: oldSubFocusItem)->focus = false; |
| 1155 | changed << oldSubFocusItem; |
| 1156 | } |
| 1157 | |
| 1158 | QQuickItemPrivate::get(item)->updateSubFocusItem(scope, focus: true); |
| 1159 | } |
| 1160 | |
| 1161 | if (!(options & DontChangeFocusProperty)) { |
| 1162 | if (item != contentItem |
| 1163 | || windowHasFocus(win: q) |
| 1164 | #ifdef Q_OS_WEBOS |
| 1165 | // Allow focused if there is only one window in the screen where it belongs. |
| 1166 | // Temporary fix for webOS until multi-seat is implemented see QTBUG-85272 |
| 1167 | || singleWindowOnScreen(q) |
| 1168 | #endif |
| 1169 | ) { |
| 1170 | itemPrivate->focus = true; |
| 1171 | changed << item; |
| 1172 | } |
| 1173 | } |
| 1174 | |
| 1175 | if (newActiveFocusItem && contentItem->hasFocus()) { |
| 1176 | activeFocusItem = newActiveFocusItem; |
| 1177 | |
| 1178 | QQuickItemPrivate::get(item: newActiveFocusItem)->activeFocus = true; |
| 1179 | changed << newActiveFocusItem; |
| 1180 | |
| 1181 | QQuickItem *afi = newActiveFocusItem->parentItem(); |
| 1182 | while (afi && afi != scope) { |
| 1183 | if (afi->isFocusScope()) { |
| 1184 | QQuickItemPrivate::get(item: afi)->activeFocus = true; |
| 1185 | changed << afi; |
| 1186 | } |
| 1187 | afi = afi->parentItem(); |
| 1188 | } |
| 1189 | updateFocusItemTransform(); |
| 1190 | sendFocusIn = true; |
| 1191 | } |
| 1192 | |
| 1193 | // Now that all the state is changed, emit signals & events |
| 1194 | // We must do this last, as this process may result in further changes to focus. |
| 1195 | if (oldActiveFocusItem) { |
| 1196 | QFocusEvent event(QEvent::FocusOut, reason); |
| 1197 | QCoreApplication::sendEvent(receiver: oldActiveFocusItem, event: &event); |
| 1198 | } |
| 1199 | |
| 1200 | // Make sure that the FocusOut didn't result in another focus change. |
| 1201 | if (sendFocusIn && activeFocusItem == newActiveFocusItem) { |
| 1202 | QFocusEvent event(QEvent::FocusIn, reason); |
| 1203 | QCoreApplication::sendEvent(receiver: newActiveFocusItem, event: &event); |
| 1204 | } |
| 1205 | |
| 1206 | if (activeFocusItem != currentActiveFocusItem) |
| 1207 | emit q->focusObjectChanged(object: activeFocusItem); |
| 1208 | |
| 1209 | if (!changed.isEmpty()) |
| 1210 | notifyFocusChangesRecur(item: changed.data(), remaining: changed.count() - 1); |
| 1211 | } |
| 1212 | |
| 1213 | void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions options) |
| 1214 | { |
| 1215 | Q_Q(QQuickWindow); |
| 1216 | |
| 1217 | Q_ASSERT(item); |
| 1218 | Q_ASSERT(scope || item == contentItem); |
| 1219 | |
| 1220 | qCDebug(DBG_FOCUS) << "QQuickWindowPrivate::clearFocusInScope():" ; |
| 1221 | qCDebug(DBG_FOCUS) << " scope:" << (QObject *)scope; |
| 1222 | qCDebug(DBG_FOCUS) << " item:" << (QObject *)item; |
| 1223 | qCDebug(DBG_FOCUS) << " activeFocusItem:" << (QObject *)activeFocusItem; |
| 1224 | |
| 1225 | QQuickItemPrivate *scopePrivate = nullptr; |
| 1226 | if (scope) { |
| 1227 | scopePrivate = QQuickItemPrivate::get(item: scope); |
| 1228 | if ( !scopePrivate->subFocusItem ) |
| 1229 | return;//No focus, nothing to do. |
| 1230 | } |
| 1231 | |
| 1232 | QQuickItem *currentActiveFocusItem = activeFocusItem; |
| 1233 | QQuickItem *oldActiveFocusItem = nullptr; |
| 1234 | QQuickItem *newActiveFocusItem = nullptr; |
| 1235 | |
| 1236 | lastFocusReason = reason; |
| 1237 | |
| 1238 | QVarLengthArray<QQuickItem *, 20> changed; |
| 1239 | |
| 1240 | Q_ASSERT(item == contentItem || item == scopePrivate->subFocusItem); |
| 1241 | |
| 1242 | // Does this change the active focus? |
| 1243 | if (item == contentItem || scopePrivate->activeFocus) { |
| 1244 | oldActiveFocusItem = activeFocusItem; |
| 1245 | newActiveFocusItem = scope; |
| 1246 | |
| 1247 | #if QT_CONFIG(im) |
| 1248 | QGuiApplication::inputMethod()->commit(); |
| 1249 | #endif |
| 1250 | |
| 1251 | activeFocusItem = nullptr; |
| 1252 | |
| 1253 | if (oldActiveFocusItem) { |
| 1254 | QQuickItem *afi = oldActiveFocusItem; |
| 1255 | while (afi && afi != scope) { |
| 1256 | if (QQuickItemPrivate::get(item: afi)->activeFocus) { |
| 1257 | QQuickItemPrivate::get(item: afi)->activeFocus = false; |
| 1258 | changed << afi; |
| 1259 | } |
| 1260 | afi = afi->parentItem(); |
| 1261 | } |
| 1262 | } |
| 1263 | } |
| 1264 | |
| 1265 | if (item != contentItem && !(options & DontChangeSubFocusItem)) { |
| 1266 | QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem; |
| 1267 | if (oldSubFocusItem && !(options & DontChangeFocusProperty)) { |
| 1268 | QQuickItemPrivate::get(item: oldSubFocusItem)->focus = false; |
| 1269 | changed << oldSubFocusItem; |
| 1270 | } |
| 1271 | |
| 1272 | QQuickItemPrivate::get(item)->updateSubFocusItem(scope, focus: false); |
| 1273 | |
| 1274 | } else if (!(options & DontChangeFocusProperty)) { |
| 1275 | QQuickItemPrivate::get(item)->focus = false; |
| 1276 | changed << item; |
| 1277 | } |
| 1278 | |
| 1279 | if (newActiveFocusItem) { |
| 1280 | Q_ASSERT(newActiveFocusItem == scope); |
| 1281 | activeFocusItem = scope; |
| 1282 | updateFocusItemTransform(); |
| 1283 | } |
| 1284 | |
| 1285 | // Now that all the state is changed, emit signals & events |
| 1286 | // We must do this last, as this process may result in further changes to |
| 1287 | // focus. |
| 1288 | if (oldActiveFocusItem) { |
| 1289 | QFocusEvent event(QEvent::FocusOut, reason); |
| 1290 | QCoreApplication::sendEvent(receiver: oldActiveFocusItem, event: &event); |
| 1291 | } |
| 1292 | |
| 1293 | // Make sure that the FocusOut didn't result in another focus change. |
| 1294 | if (newActiveFocusItem && activeFocusItem == newActiveFocusItem) { |
| 1295 | QFocusEvent event(QEvent::FocusIn, reason); |
| 1296 | QCoreApplication::sendEvent(receiver: newActiveFocusItem, event: &event); |
| 1297 | } |
| 1298 | |
| 1299 | if (activeFocusItem != currentActiveFocusItem) |
| 1300 | emit q->focusObjectChanged(object: activeFocusItem); |
| 1301 | |
| 1302 | if (!changed.isEmpty()) |
| 1303 | notifyFocusChangesRecur(item: changed.data(), remaining: changed.count() - 1); |
| 1304 | } |
| 1305 | |
| 1306 | void QQuickWindowPrivate::clearFocusObject() |
| 1307 | { |
| 1308 | if (activeFocusItem == contentItem) |
| 1309 | return; |
| 1310 | |
| 1311 | clearFocusInScope(scope: contentItem, item: QQuickItemPrivate::get(item: contentItem)->subFocusItem, reason: Qt::OtherFocusReason); |
| 1312 | } |
| 1313 | |
| 1314 | void QQuickWindowPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining) |
| 1315 | { |
| 1316 | QPointer<QQuickItem> item(*items); |
| 1317 | |
| 1318 | if (remaining) |
| 1319 | notifyFocusChangesRecur(items: items + 1, remaining: remaining - 1); |
| 1320 | |
| 1321 | if (item) { |
| 1322 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
| 1323 | |
| 1324 | if (itemPrivate->notifiedFocus != itemPrivate->focus) { |
| 1325 | itemPrivate->notifiedFocus = itemPrivate->focus; |
| 1326 | emit item->focusChanged(itemPrivate->focus); |
| 1327 | } |
| 1328 | |
| 1329 | if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) { |
| 1330 | itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus; |
| 1331 | itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus); |
| 1332 | emit item->activeFocusChanged(itemPrivate->activeFocus); |
| 1333 | } |
| 1334 | } |
| 1335 | } |
| 1336 | |
| 1337 | void QQuickWindowPrivate::dirtyItem(QQuickItem *) |
| 1338 | { |
| 1339 | Q_Q(QQuickWindow); |
| 1340 | q->maybeUpdate(); |
| 1341 | } |
| 1342 | |
| 1343 | void QQuickWindowPrivate::cleanup(QSGNode *n) |
| 1344 | { |
| 1345 | Q_Q(QQuickWindow); |
| 1346 | |
| 1347 | Q_ASSERT(!cleanupNodeList.contains(n)); |
| 1348 | cleanupNodeList.append(t: n); |
| 1349 | q->maybeUpdate(); |
| 1350 | } |
| 1351 | |
| 1352 | /*! |
| 1353 | \qmltype Window |
| 1354 | \instantiates QQuickWindow |
| 1355 | \inqmlmodule QtQuick.Window |
| 1356 | \ingroup qtquick-visual |
| 1357 | \brief Creates a new top-level window. |
| 1358 | |
| 1359 | The Window object creates a new top-level window for a Qt Quick scene. It automatically sets up the |
| 1360 | window for use with \c {QtQuick 2.x} graphical types. |
| 1361 | |
| 1362 | To use this type, you will need to import the module with the following line: |
| 1363 | \code |
| 1364 | import QtQuick.Window 2.2 |
| 1365 | \endcode |
| 1366 | |
| 1367 | Omitting this import will allow you to have a QML environment without |
| 1368 | access to window system features. |
| 1369 | |
| 1370 | A Window can be declared inside an Item or inside another Window; in that |
| 1371 | case the inner Window will automatically become "transient for" the outer |
| 1372 | Window: that is, most platforms will show it centered upon the outer window |
| 1373 | by default, and there may be other platform-dependent behaviors, depending |
| 1374 | also on the \l flags. If the nested window is intended to be a dialog in |
| 1375 | your application, you should also set \l flags to Qt.Dialog, because some |
| 1376 | window managers will not provide the centering behavior without that flag. |
| 1377 | You can also declare multiple windows inside a top-level \l QtObject, in which |
| 1378 | case the windows will have no transient relationship. |
| 1379 | |
| 1380 | Alternatively you can set or bind \l x and \l y to position the Window |
| 1381 | explicitly on the screen. |
| 1382 | |
| 1383 | When the user attempts to close a window, the \l closing signal will be |
| 1384 | emitted. You can force the window to stay open (for example to prompt the |
| 1385 | user to save changes) by writing an \c onClosing handler and setting |
| 1386 | \c {close.accepted = false}. |
| 1387 | */ |
| 1388 | /*! |
| 1389 | \class QQuickWindow |
| 1390 | \since 5.0 |
| 1391 | |
| 1392 | \inmodule QtQuick |
| 1393 | |
| 1394 | \brief The QQuickWindow class provides the window for displaying a graphical QML scene. |
| 1395 | |
| 1396 | QQuickWindow provides the graphical scene management needed to interact with and display |
| 1397 | a scene of QQuickItems. |
| 1398 | |
| 1399 | A QQuickWindow always has a single invisible root item. To add items to this window, |
| 1400 | reparent the items to the root item or to an existing item in the scene. |
| 1401 | |
| 1402 | For easily displaying a scene from a QML file, see \l{QQuickView}. |
| 1403 | |
| 1404 | \section1 Rendering |
| 1405 | |
| 1406 | QQuickWindow uses a scene graph to represent what needs to be rendered. |
| 1407 | This scene graph is disconnected from the QML scene and |
| 1408 | potentially lives in another thread, depending on the platform |
| 1409 | implementation. Since the rendering scene graph lives |
| 1410 | independently from the QML scene, it can also be completely |
| 1411 | released without affecting the state of the QML scene. |
| 1412 | |
| 1413 | The sceneGraphInitialized() signal is emitted on the rendering |
| 1414 | thread before the QML scene is rendered to the screen for the |
| 1415 | first time. If the rendering scene graph has been released, the |
| 1416 | signal will be emitted again before the next frame is rendered. |
| 1417 | |
| 1418 | |
| 1419 | \section2 Integration with OpenGL |
| 1420 | |
| 1421 | When using the default OpenGL adaptation, it is possible to integrate |
| 1422 | OpenGL calls directly into the QQuickWindow using the same OpenGL |
| 1423 | context as the Qt Quick Scene Graph. This is done by connecting to the |
| 1424 | QQuickWindow::beforeRendering() or QQuickWindow::afterRendering() |
| 1425 | signal. |
| 1426 | |
| 1427 | \note When using QQuickWindow::beforeRendering(), make sure to |
| 1428 | disable clearing before rendering with |
| 1429 | QQuickWindow::setClearBeforeRendering(). |
| 1430 | |
| 1431 | |
| 1432 | \section2 Exposure and Visibility |
| 1433 | |
| 1434 | When a QQuickWindow instance is deliberately hidden with hide() or |
| 1435 | setVisible(false), it will stop rendering and its scene graph and |
| 1436 | graphics context might be released. The sceneGraphInvalidated() |
| 1437 | signal will be emitted when this happens. |
| 1438 | |
| 1439 | \warning It is crucial that graphics operations and interaction with |
| 1440 | the scene graph happens exclusively on the rendering thread, |
| 1441 | primarily during the updatePaintNode() phase. |
| 1442 | |
| 1443 | \warning As signals related to rendering might be emitted from the |
| 1444 | rendering thread, connections should be made using |
| 1445 | Qt::DirectConnection. |
| 1446 | |
| 1447 | |
| 1448 | \section2 Resource Management |
| 1449 | |
| 1450 | QML will try to cache images and scene graph nodes to |
| 1451 | improve performance, but in some low-memory scenarios it might be |
| 1452 | required to aggressively release these resources. The |
| 1453 | releaseResources() can be used to force the clean up of certain |
| 1454 | resources. Calling releaseResources() may result in the entire |
| 1455 | scene graph and in the case of the OpenGL adaptation the associated |
| 1456 | context will be deleted. The sceneGraphInvalidated() signal will be |
| 1457 | emitted when this happens. |
| 1458 | |
| 1459 | \note All classes with QSG prefix should be used solely on the scene graph's |
| 1460 | rendering thread. See \l {Scene Graph and Rendering} for more information. |
| 1461 | |
| 1462 | \section2 Context and Surface Formats |
| 1463 | |
| 1464 | While it is possible to specify a QSurfaceFormat for every QQuickWindow by |
| 1465 | calling the member function setFormat(), windows may also be created from |
| 1466 | QML by using the Window and ApplicationWindow elements. In this case there |
| 1467 | is no C++ code involved in the creation of the window instance, yet |
| 1468 | applications may still wish to set certain surface format values, for |
| 1469 | example to request a given OpenGL version or profile. Such applications can |
| 1470 | call the static function QSurfaceFormat::setDefaultFormat() at startup. The |
| 1471 | specified format will be used for all Quick windows created afterwards. |
| 1472 | |
| 1473 | \sa {Scene Graph - OpenGL Under QML} |
| 1474 | */ |
| 1475 | |
| 1476 | /*! |
| 1477 | Constructs a window for displaying a QML scene with parent window \a parent. |
| 1478 | */ |
| 1479 | QQuickWindow::QQuickWindow(QWindow *parent) |
| 1480 | : QQuickWindow(*new QQuickWindowPrivate, parent) |
| 1481 | { |
| 1482 | } |
| 1483 | |
| 1484 | |
| 1485 | |
| 1486 | /*! |
| 1487 | \internal |
| 1488 | */ |
| 1489 | QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QWindow *parent) |
| 1490 | : QWindow(dd, parent) |
| 1491 | { |
| 1492 | Q_D(QQuickWindow); |
| 1493 | d->init(c: this); |
| 1494 | } |
| 1495 | |
| 1496 | /*! |
| 1497 | \internal |
| 1498 | */ |
| 1499 | QQuickWindow::QQuickWindow(QQuickRenderControl *control) |
| 1500 | : QWindow(*(new QQuickWindowPrivate), nullptr) |
| 1501 | { |
| 1502 | Q_D(QQuickWindow); |
| 1503 | d->init(c: this, control); |
| 1504 | } |
| 1505 | |
| 1506 | /*! |
| 1507 | \internal |
| 1508 | */ |
| 1509 | QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QQuickRenderControl *control) |
| 1510 | : QWindow(dd, nullptr) |
| 1511 | { |
| 1512 | Q_D(QQuickWindow); |
| 1513 | d->init(c: this, control); |
| 1514 | } |
| 1515 | |
| 1516 | /*! |
| 1517 | Destroys the window. |
| 1518 | */ |
| 1519 | QQuickWindow::~QQuickWindow() |
| 1520 | { |
| 1521 | Q_D(QQuickWindow); |
| 1522 | |
| 1523 | if (d->renderControl) { |
| 1524 | QQuickRenderControlPrivate::get(renderControl: d->renderControl)->windowDestroyed(); |
| 1525 | } else if (d->windowManager) { |
| 1526 | d->windowManager->removeWindow(win: this); |
| 1527 | d->windowManager->windowDestroyed(window: this); |
| 1528 | } |
| 1529 | |
| 1530 | delete d->incubationController; d->incubationController = nullptr; |
| 1531 | #if QT_CONFIG(quick_draganddrop) |
| 1532 | delete d->dragGrabber; d->dragGrabber = nullptr; |
| 1533 | #endif |
| 1534 | QQuickRootItem *root = d->contentItem; |
| 1535 | d->contentItem = nullptr; |
| 1536 | delete root; |
| 1537 | qDeleteAll(c: d->pointerEventInstances); |
| 1538 | d->pointerEventInstances.clear(); |
| 1539 | |
| 1540 | d->renderJobMutex.lock(); |
| 1541 | qDeleteAll(c: d->beforeSynchronizingJobs); |
| 1542 | d->beforeSynchronizingJobs.clear(); |
| 1543 | qDeleteAll(c: d->afterSynchronizingJobs); |
| 1544 | d->afterSynchronizingJobs.clear(); |
| 1545 | qDeleteAll(c: d->beforeRenderingJobs); |
| 1546 | d->beforeRenderingJobs.clear(); |
| 1547 | qDeleteAll(c: d->afterRenderingJobs); |
| 1548 | d->afterRenderingJobs.clear(); |
| 1549 | qDeleteAll(c: d->afterSwapJobs); |
| 1550 | d->afterSwapJobs.clear(); |
| 1551 | d->renderJobMutex.unlock(); |
| 1552 | |
| 1553 | // It is important that the pixmap cache is cleaned up during shutdown. |
| 1554 | // Besides playing nice, this also solves a practical problem that |
| 1555 | // QQuickTextureFactory implementations in other libraries need |
| 1556 | // have their destructors loaded while they the library is still |
| 1557 | // loaded into memory. |
| 1558 | QQuickPixmap::purgeCache(); |
| 1559 | } |
| 1560 | |
| 1561 | /*! |
| 1562 | This function tries to release redundant resources currently held by the QML scene. |
| 1563 | |
| 1564 | Calling this function might result in the scene graph and the OpenGL context used |
| 1565 | for rendering being released to release graphics memory. If this happens, the |
| 1566 | sceneGraphInvalidated() signal will be called, allowing users to clean up their |
| 1567 | own graphics resources. The setPersistentOpenGLContext() and setPersistentSceneGraph() |
| 1568 | functions can be used to prevent this from happening, if handling the cleanup is |
| 1569 | not feasible in the application, at the cost of higher memory usage. |
| 1570 | |
| 1571 | \sa sceneGraphInvalidated(), setPersistentOpenGLContext(), setPersistentSceneGraph() |
| 1572 | */ |
| 1573 | |
| 1574 | void QQuickWindow::releaseResources() |
| 1575 | { |
| 1576 | Q_D(QQuickWindow); |
| 1577 | if (d->windowManager) |
| 1578 | d->windowManager->releaseResources(window: this); |
| 1579 | QQuickPixmap::purgeCache(); |
| 1580 | } |
| 1581 | |
| 1582 | |
| 1583 | |
| 1584 | /*! |
| 1585 | Sets whether the OpenGL context should be preserved, and cannot be |
| 1586 | released until the last window is deleted, to \a persistent. The |
| 1587 | default value is true. |
| 1588 | |
| 1589 | The OpenGL context can be released to free up graphics resources |
| 1590 | when the window is obscured, hidden or not rendering. When this |
| 1591 | happens is implementation specific. |
| 1592 | |
| 1593 | The QOpenGLContext::aboutToBeDestroyed() signal is emitted from |
| 1594 | the QQuickWindow::openglContext() when the OpenGL context is about |
| 1595 | to be released. The QQuickWindow::sceneGraphInitialized() signal |
| 1596 | is emitted when a new OpenGL context is created for this |
| 1597 | window. Make a Qt::DirectConnection to these signals to be |
| 1598 | notified. |
| 1599 | |
| 1600 | The OpenGL context is still released when the last QQuickWindow is |
| 1601 | deleted. |
| 1602 | |
| 1603 | \note This only has an effect when using the default OpenGL scene |
| 1604 | graph adaptation. |
| 1605 | |
| 1606 | \sa setPersistentSceneGraph(), |
| 1607 | QOpenGLContext::aboutToBeDestroyed(), sceneGraphInitialized() |
| 1608 | */ |
| 1609 | |
| 1610 | void QQuickWindow::setPersistentOpenGLContext(bool persistent) |
| 1611 | { |
| 1612 | Q_D(QQuickWindow); |
| 1613 | d->persistentGLContext = persistent; |
| 1614 | } |
| 1615 | |
| 1616 | |
| 1617 | |
| 1618 | /*! |
| 1619 | Returns whether the OpenGL context can be released during the |
| 1620 | lifetime of the QQuickWindow. |
| 1621 | |
| 1622 | \note This is a hint. When and how this happens is implementation |
| 1623 | specific. It also only has an effect when using the default OpenGL |
| 1624 | scene graph adaptation |
| 1625 | */ |
| 1626 | |
| 1627 | bool QQuickWindow::isPersistentOpenGLContext() const |
| 1628 | { |
| 1629 | Q_D(const QQuickWindow); |
| 1630 | return d->persistentGLContext; |
| 1631 | } |
| 1632 | |
| 1633 | |
| 1634 | |
| 1635 | /*! |
| 1636 | Sets whether the scene graph nodes and resources are \a persistent. |
| 1637 | Persistent means the nodes and resources cannot be released. |
| 1638 | The default value is \c true. |
| 1639 | |
| 1640 | The scene graph nodes and resources can be released to free up |
| 1641 | graphics resources when the window is obscured, hidden or not |
| 1642 | rendering. When this happens is implementation specific. |
| 1643 | |
| 1644 | The QQuickWindow::sceneGraphInvalidated() signal is emitted when |
| 1645 | cleanup occurs. The QQuickWindow::sceneGraphInitialized() signal |
| 1646 | is emitted when a new scene graph is recreated for this |
| 1647 | window. Make a Qt::DirectConnection to these signals to be |
| 1648 | notified. |
| 1649 | |
| 1650 | The scene graph nodes and resources are still released when the |
| 1651 | last QQuickWindow is deleted. |
| 1652 | |
| 1653 | \sa setPersistentOpenGLContext(), |
| 1654 | sceneGraphInvalidated(), sceneGraphInitialized() |
| 1655 | */ |
| 1656 | |
| 1657 | void QQuickWindow::setPersistentSceneGraph(bool persistent) |
| 1658 | { |
| 1659 | Q_D(QQuickWindow); |
| 1660 | d->persistentSceneGraph = persistent; |
| 1661 | } |
| 1662 | |
| 1663 | |
| 1664 | |
| 1665 | /*! |
| 1666 | Returns whether the scene graph nodes and resources can be |
| 1667 | released during the lifetime of this QQuickWindow. |
| 1668 | |
| 1669 | \note This is a hint. When and how this happens is implementation |
| 1670 | specific. |
| 1671 | */ |
| 1672 | |
| 1673 | bool QQuickWindow::isPersistentSceneGraph() const |
| 1674 | { |
| 1675 | Q_D(const QQuickWindow); |
| 1676 | return d->persistentSceneGraph; |
| 1677 | } |
| 1678 | |
| 1679 | |
| 1680 | |
| 1681 | |
| 1682 | /*! |
| 1683 | \qmlattachedproperty Item Window::contentItem |
| 1684 | \since 5.4 |
| 1685 | |
| 1686 | This attached property holds the invisible root item of the scene or |
| 1687 | \c null if the item is not in a window. The Window attached property |
| 1688 | can be attached to any Item. |
| 1689 | */ |
| 1690 | |
| 1691 | /*! |
| 1692 | \property QQuickWindow::contentItem |
| 1693 | \brief The invisible root item of the scene. |
| 1694 | |
| 1695 | A QQuickWindow always has a single invisible root item containing all of its content. |
| 1696 | To add items to this window, reparent the items to the contentItem or to an existing |
| 1697 | item in the scene. |
| 1698 | */ |
| 1699 | QQuickItem *QQuickWindow::contentItem() const |
| 1700 | { |
| 1701 | Q_D(const QQuickWindow); |
| 1702 | |
| 1703 | return d->contentItem; |
| 1704 | } |
| 1705 | |
| 1706 | /*! |
| 1707 | \property QQuickWindow::activeFocusItem |
| 1708 | |
| 1709 | \brief The item which currently has active focus or \c null if there is |
| 1710 | no item with active focus. |
| 1711 | |
| 1712 | \sa QQuickItem::forceActiveFocus(), {Keyboard Focus in Qt Quick} |
| 1713 | */ |
| 1714 | QQuickItem *QQuickWindow::activeFocusItem() const |
| 1715 | { |
| 1716 | Q_D(const QQuickWindow); |
| 1717 | |
| 1718 | return d->activeFocusItem; |
| 1719 | } |
| 1720 | |
| 1721 | /*! |
| 1722 | \internal |
| 1723 | \reimp |
| 1724 | */ |
| 1725 | QObject *QQuickWindow::focusObject() const |
| 1726 | { |
| 1727 | Q_D(const QQuickWindow); |
| 1728 | |
| 1729 | if (d->activeFocusItem) |
| 1730 | return d->activeFocusItem; |
| 1731 | return const_cast<QQuickWindow*>(this); |
| 1732 | } |
| 1733 | |
| 1734 | |
| 1735 | /*! |
| 1736 | Returns the item which currently has the mouse grab. |
| 1737 | */ |
| 1738 | QQuickItem *QQuickWindow::mouseGrabberItem() const |
| 1739 | { |
| 1740 | Q_D(const QQuickWindow); |
| 1741 | |
| 1742 | if (d->isDeliveringTouchAsMouse()) { |
| 1743 | if (QQuickPointerEvent *event = d->queryPointerEventInstance(device: d->touchMouseDevice)) { |
| 1744 | auto point = event->pointById(pointId: d->touchMouseId); |
| 1745 | return point ? point->grabberItem() : nullptr; |
| 1746 | } |
| 1747 | } else if (QQuickPointerEvent *event = d->queryPointerEventInstance(device: QQuickPointerDevice::genericMouseDevice())) { |
| 1748 | Q_ASSERT(event->pointCount()); |
| 1749 | return event->point(i: 0)->grabberItem(); |
| 1750 | } |
| 1751 | return nullptr; |
| 1752 | } |
| 1753 | |
| 1754 | |
| 1755 | bool QQuickWindowPrivate::clearHover(ulong timestamp) |
| 1756 | { |
| 1757 | Q_Q(QQuickWindow); |
| 1758 | if (hoverItems.isEmpty()) |
| 1759 | return false; |
| 1760 | |
| 1761 | QPointF pos = q->mapFromGlobal(pos: QGuiApplicationPrivate::lastCursorPosition.toPoint()); |
| 1762 | |
| 1763 | bool accepted = false; |
| 1764 | for (QQuickItem* item : qAsConst(t&: hoverItems)) { |
| 1765 | accepted = sendHoverEvent(QEvent::HoverLeave, item, scenePos: pos, lastScenePos: pos, modifiers: QGuiApplication::keyboardModifiers(), timestamp, accepted: true) || accepted; |
| 1766 | #if QT_CONFIG(cursor) |
| 1767 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
| 1768 | if (itemPrivate->hasPointerHandlers()) { |
| 1769 | pos = q->mapFromGlobal(pos: QCursor::pos()); |
| 1770 | QQuickPointerEvent *pointerEvent = pointerEventInstance(device: QQuickPointerDevice::genericMouseDevice(), eventType: QEvent::MouseMove); |
| 1771 | pointerEvent->point(i: 0)->reset(state: Qt::TouchPointMoved, scenePosition: pos, pointId: quint64(1) << 24 /* mouse has device ID 1 */, timestamp, velocity: QVector2D()); |
| 1772 | pointerEvent->point(i: 0)->setAccepted(true); |
| 1773 | pointerEvent->localize(target: item); |
| 1774 | for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers) |
| 1775 | if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(object: h)) |
| 1776 | hh->handlePointerEvent(event: pointerEvent); |
| 1777 | } |
| 1778 | #endif |
| 1779 | } |
| 1780 | hoverItems.clear(); |
| 1781 | return accepted; |
| 1782 | } |
| 1783 | |
| 1784 | /*! \reimp */ |
| 1785 | bool QQuickWindow::event(QEvent *e) |
| 1786 | { |
| 1787 | Q_D(QQuickWindow); |
| 1788 | |
| 1789 | switch (e->type()) { |
| 1790 | |
| 1791 | case QEvent::TouchBegin: |
| 1792 | case QEvent::TouchUpdate: |
| 1793 | case QEvent::TouchEnd: { |
| 1794 | QTouchEvent *touch = static_cast<QTouchEvent*>(e); |
| 1795 | d->handleTouchEvent(touch); |
| 1796 | if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) { |
| 1797 | // we consume all touch events ourselves to avoid duplicate |
| 1798 | // mouse delivery by QtGui mouse synthesis |
| 1799 | e->accept(); |
| 1800 | } |
| 1801 | return true; |
| 1802 | } |
| 1803 | break; |
| 1804 | case QEvent::TouchCancel: |
| 1805 | // return in order to avoid the QWindow::event below |
| 1806 | return d->deliverTouchCancelEvent(static_cast<QTouchEvent*>(e)); |
| 1807 | break; |
| 1808 | case QEvent::Enter: { |
| 1809 | if (!d->contentItem) |
| 1810 | return false; |
| 1811 | QEnterEvent *enter = static_cast<QEnterEvent*>(e); |
| 1812 | bool accepted = enter->isAccepted(); |
| 1813 | bool delivered = d->deliverHoverEvent(d->contentItem, scenePos: enter->windowPos(), lastScenePos: d->lastMousePosition, |
| 1814 | modifiers: QGuiApplication::keyboardModifiers(), timestamp: 0L, accepted); |
| 1815 | d->lastMousePosition = enter->windowPos(); |
| 1816 | enter->setAccepted(accepted); |
| 1817 | #if QT_CONFIG(cursor) |
| 1818 | d->updateCursor(scenePos: mapFromGlobal(pos: QCursor::pos())); |
| 1819 | #endif |
| 1820 | return delivered; |
| 1821 | } |
| 1822 | break; |
| 1823 | case QEvent::Leave: |
| 1824 | d->clearHover(); |
| 1825 | d->lastMousePosition = QPointF(); |
| 1826 | break; |
| 1827 | #if QT_CONFIG(quick_draganddrop) |
| 1828 | case QEvent::DragEnter: |
| 1829 | case QEvent::DragLeave: |
| 1830 | case QEvent::DragMove: |
| 1831 | case QEvent::Drop: |
| 1832 | d->deliverDragEvent(d->dragGrabber, e); |
| 1833 | break; |
| 1834 | #endif |
| 1835 | case QEvent::WindowDeactivate: |
| 1836 | if (d->contentItem) |
| 1837 | d->contentItem->windowDeactivateEvent(); |
| 1838 | break; |
| 1839 | case QEvent::Close: { |
| 1840 | // TOOD Qt 6 (binary incompatible) |
| 1841 | // closeEvent(static_cast<QCloseEvent *>(e)); |
| 1842 | QQuickCloseEvent qev; |
| 1843 | qev.setAccepted(e->isAccepted()); |
| 1844 | emit closing(close: &qev); |
| 1845 | e->setAccepted(qev.isAccepted()); |
| 1846 | } break; |
| 1847 | case QEvent::PlatformSurface: |
| 1848 | if ((static_cast<QPlatformSurfaceEvent *>(e))->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) { |
| 1849 | // Ensure that the rendering thread is notified before |
| 1850 | // the QPlatformWindow is destroyed. |
| 1851 | if (d->windowManager) |
| 1852 | d->windowManager->hide(window: this); |
| 1853 | } |
| 1854 | break; |
| 1855 | case QEvent::FocusAboutToChange: |
| 1856 | #if QT_CONFIG(im) |
| 1857 | if (d->activeFocusItem) |
| 1858 | qGuiApp->inputMethod()->commit(); |
| 1859 | #endif |
| 1860 | if (mouseGrabberItem()) |
| 1861 | mouseGrabberItem()->ungrabMouse(); |
| 1862 | break; |
| 1863 | case QEvent::UpdateRequest: { |
| 1864 | if (d->windowManager) |
| 1865 | d->windowManager->handleUpdateRequest(this); |
| 1866 | break; |
| 1867 | } |
| 1868 | #if QT_CONFIG(gestures) |
| 1869 | case QEvent::NativeGesture: |
| 1870 | d->deliverSinglePointEventUntilAccepted(d->pointerEventInstance(ev: e)); |
| 1871 | break; |
| 1872 | #endif |
| 1873 | case QEvent::ShortcutOverride: |
| 1874 | if (d->activeFocusItem) |
| 1875 | QCoreApplication::sendEvent(receiver: d->activeFocusItem, event: e); |
| 1876 | return true; |
| 1877 | case QEvent::LanguageChange: |
| 1878 | if (d->contentItem) |
| 1879 | QCoreApplication::sendEvent(receiver: d->contentItem, event: e); |
| 1880 | break; |
| 1881 | case QEvent::InputMethod: |
| 1882 | case QEvent::InputMethodQuery: |
| 1883 | { |
| 1884 | QQuickItem *target = d->activeFocusItem; |
| 1885 | // while an input method delivers the event, this window might still be inactive |
| 1886 | if (!target) { |
| 1887 | target = d->contentItem; |
| 1888 | if (!target || !target->isEnabled()) |
| 1889 | break; |
| 1890 | // see setFocusInScope for a similar loop |
| 1891 | while (target->isFocusScope() && target->scopedFocusItem() && target->scopedFocusItem()->isEnabled()) |
| 1892 | target = target->scopedFocusItem(); |
| 1893 | } |
| 1894 | if (target) { |
| 1895 | QCoreApplication::sendEvent(receiver: target, event: e); |
| 1896 | return true; |
| 1897 | } |
| 1898 | } |
| 1899 | break; |
| 1900 | default: |
| 1901 | break; |
| 1902 | } |
| 1903 | |
| 1904 | if (e->type() == QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)) |
| 1905 | update(); |
| 1906 | else if (e->type() == QEvent::Type(QQuickWindowPrivate::TriggerContextCreationFailure)) |
| 1907 | d->windowManager->handleContextCreationFailure(window: this); |
| 1908 | |
| 1909 | return QWindow::event(e); |
| 1910 | } |
| 1911 | |
| 1912 | /*! \reimp */ |
| 1913 | void QQuickWindow::keyPressEvent(QKeyEvent *e) |
| 1914 | { |
| 1915 | Q_D(QQuickWindow); |
| 1916 | Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyPress, e->key(), |
| 1917 | e->modifiers()); |
| 1918 | d->deliverKeyEvent(e); |
| 1919 | } |
| 1920 | |
| 1921 | /*! \reimp */ |
| 1922 | void QQuickWindow::keyReleaseEvent(QKeyEvent *e) |
| 1923 | { |
| 1924 | Q_D(QQuickWindow); |
| 1925 | Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyRelease, e->key(), |
| 1926 | e->modifiers()); |
| 1927 | d->deliverKeyEvent(e); |
| 1928 | } |
| 1929 | |
| 1930 | void QQuickWindowPrivate::deliverKeyEvent(QKeyEvent *e) |
| 1931 | { |
| 1932 | if (activeFocusItem) { |
| 1933 | QQuickItem *item = activeFocusItem; |
| 1934 | |
| 1935 | // In case of generated event, trigger ShortcutOverride event |
| 1936 | if (e->type() == QEvent::KeyPress && e->spontaneous() == false) |
| 1937 | qt_sendShortcutOverrideEvent(o: item, timestamp: e->timestamp(), |
| 1938 | k: e->key(), mods: e->modifiers(), text: e->text(), |
| 1939 | autorep: e->isAutoRepeat(), count: e->count()); |
| 1940 | |
| 1941 | e->accept(); |
| 1942 | QCoreApplication::sendEvent(receiver: item, event: e); |
| 1943 | while (!e->isAccepted() && (item = item->parentItem())) { |
| 1944 | e->accept(); |
| 1945 | QCoreApplication::sendEvent(receiver: item, event: e); |
| 1946 | } |
| 1947 | } |
| 1948 | } |
| 1949 | |
| 1950 | QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos) |
| 1951 | { |
| 1952 | int caps = QGuiApplicationPrivate::mouseEventCaps(event); |
| 1953 | QVector2D velocity = QGuiApplicationPrivate::mouseEventVelocity(event); |
| 1954 | QMouseEvent *me = new QMouseEvent(event->type(), |
| 1955 | transformedLocalPos ? *transformedLocalPos : event->localPos(), |
| 1956 | event->windowPos(), event->screenPos(), |
| 1957 | event->button(), event->buttons(), event->modifiers()); |
| 1958 | QGuiApplicationPrivate::setMouseEventCapsAndVelocity(event: me, caps, velocity); |
| 1959 | QGuiApplicationPrivate::setMouseEventSource(event: me, source: QGuiApplicationPrivate::mouseEventSource(event)); |
| 1960 | me->setTimestamp(event->timestamp()); |
| 1961 | return me; |
| 1962 | } |
| 1963 | |
| 1964 | void QQuickWindowPrivate::deliverToPassiveGrabbers(const QVector<QPointer <QQuickPointerHandler> > &passiveGrabbers, |
| 1965 | QQuickPointerEvent *pointerEvent) |
| 1966 | { |
| 1967 | const QVector<QQuickPointerHandler *> &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets(); |
| 1968 | QVarLengthArray<QPair<QQuickItem *, bool>, 4> sendFilteredPointerEventResult; |
| 1969 | hasFiltered.clear(); |
| 1970 | for (auto handler : passiveGrabbers) { |
| 1971 | // a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically |
| 1972 | if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(t: handler)) { |
| 1973 | bool alreadyFiltered = false; |
| 1974 | QQuickItem *par = handler->parentItem(); |
| 1975 | |
| 1976 | // see if we already have sent a filter event to the parent |
| 1977 | auto it = std::find_if(first: sendFilteredPointerEventResult.begin(), last: sendFilteredPointerEventResult.end(), |
| 1978 | pred: [par](const QPair<QQuickItem *, bool> &pair) { return pair.first == par; }); |
| 1979 | if (it != sendFilteredPointerEventResult.end()) { |
| 1980 | // Yes, the event was already filtered to that parent, do not call it again but use |
| 1981 | // the result of the previous call to determine if we should call the handler. |
| 1982 | alreadyFiltered = it->second; |
| 1983 | } else { |
| 1984 | alreadyFiltered = sendFilteredPointerEvent(event: pointerEvent, receiver: par); |
| 1985 | sendFilteredPointerEventResult << qMakePair<QQuickItem*, bool>(x: par, y: alreadyFiltered); |
| 1986 | } |
| 1987 | if (!alreadyFiltered) { |
| 1988 | pointerEvent->localize(target: handler->parentItem()); |
| 1989 | handler->handlePointerEvent(event: pointerEvent); |
| 1990 | } |
| 1991 | } |
| 1992 | } |
| 1993 | } |
| 1994 | |
| 1995 | |
| 1996 | |
| 1997 | void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent) |
| 1998 | { |
| 1999 | Q_Q(QQuickWindow); |
| 2000 | auto point = pointerEvent->point(i: 0); |
| 2001 | lastMousePosition = point->scenePosition(); |
| 2002 | const bool mouseIsReleased = (point->state() == QQuickEventPoint::Released && pointerEvent->buttons() == Qt::NoButton); |
| 2003 | QQuickItem *grabberItem = point->grabberItem(); |
| 2004 | if (!grabberItem && isDeliveringTouchAsMouse()) |
| 2005 | grabberItem = q->mouseGrabberItem(); |
| 2006 | |
| 2007 | if (grabberItem) { |
| 2008 | bool handled = false; |
| 2009 | hasFiltered.clear(); |
| 2010 | if (sendFilteredPointerEvent(event: pointerEvent, receiver: grabberItem)) |
| 2011 | handled = true; |
| 2012 | // if the grabber is an Item: |
| 2013 | // if the update consists of changing button state, don't accept it unless |
| 2014 | // the button is one in which the grabber is interested |
| 2015 | Qt::MouseButtons acceptedButtons = grabberItem->acceptedMouseButtons(); |
| 2016 | if (!handled && pointerEvent->button() != Qt::NoButton && acceptedButtons |
| 2017 | && !(acceptedButtons & pointerEvent->button())) { |
| 2018 | pointerEvent->setAccepted(false); |
| 2019 | handled = true; |
| 2020 | } |
| 2021 | |
| 2022 | // send update |
| 2023 | if (!handled) { |
| 2024 | QPointF localPos = grabberItem->mapFromScene(point: lastMousePosition); |
| 2025 | auto me = pointerEvent->asMouseEvent(localPos); |
| 2026 | me->accept(); |
| 2027 | QCoreApplication::sendEvent(receiver: grabberItem, event: me); |
| 2028 | point->setAccepted(me->isAccepted()); |
| 2029 | } |
| 2030 | |
| 2031 | // release event: ungrab if no buttons are pressed anymore |
| 2032 | if (mouseIsReleased) |
| 2033 | removeGrabber(grabber: grabberItem, mouse: true, touch: isDeliveringTouchAsMouse()); |
| 2034 | deliverToPassiveGrabbers(passiveGrabbers: point->passiveGrabbers(), pointerEvent); |
| 2035 | } else if (auto handler = point->grabberPointerHandler()) { |
| 2036 | pointerEvent->localize(target: handler->parentItem()); |
| 2037 | hasFiltered.clear(); |
| 2038 | if (!sendFilteredPointerEvent(event: pointerEvent, receiver: handler->parentItem())) |
| 2039 | handler->handlePointerEvent(event: pointerEvent); |
| 2040 | if (mouseIsReleased) |
| 2041 | point->setGrabberPointerHandler(exclusiveGrabber: nullptr, exclusive: true); |
| 2042 | deliverToPassiveGrabbers(passiveGrabbers: point->passiveGrabbers(), pointerEvent); |
| 2043 | } else { |
| 2044 | bool delivered = false; |
| 2045 | if (pointerEvent->isPressEvent()) { |
| 2046 | // send initial press |
| 2047 | delivered = deliverPressOrReleaseEvent(pointerEvent); |
| 2048 | } else if (pointerEvent->device()->type() == QQuickPointerDevice::Mouse) { |
| 2049 | // if this is an update or release from an actual mouse, |
| 2050 | // and the point wasn't grabbed, deliver only to PointerHandlers: |
| 2051 | // passive grabbers first, then the rest |
| 2052 | deliverToPassiveGrabbers(passiveGrabbers: point->passiveGrabbers(), pointerEvent); |
| 2053 | |
| 2054 | // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order |
| 2055 | if (!pointerEvent->allPointsGrabbed() && pointerEvent->buttons()) { |
| 2056 | QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point, checkMouseButtons: false, checkAcceptsTouch: false); |
| 2057 | for (QQuickItem *item : targetItems) { |
| 2058 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
| 2059 | if (!itemPrivate->extra.isAllocated() || itemPrivate->extra->pointerHandlers.isEmpty()) |
| 2060 | continue; |
| 2061 | pointerEvent->localize(target: item); |
| 2062 | hasFiltered.clear(); |
| 2063 | if (!sendFilteredPointerEvent(event: pointerEvent, receiver: item)) { |
| 2064 | if (itemPrivate->handlePointerEvent(pointerEvent, avoidExclusiveGrabber: true)) // avoid re-delivering to grabbers |
| 2065 | delivered = true; |
| 2066 | } |
| 2067 | if (point->exclusiveGrabber()) |
| 2068 | break; |
| 2069 | } |
| 2070 | } |
| 2071 | } |
| 2072 | |
| 2073 | if (!delivered) |
| 2074 | // make sure not to accept unhandled events |
| 2075 | pointerEvent->setAccepted(false); |
| 2076 | } |
| 2077 | } |
| 2078 | |
| 2079 | bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item, |
| 2080 | const QPointF &scenePos, const QPointF &lastScenePos, |
| 2081 | Qt::KeyboardModifiers modifiers, ulong timestamp, |
| 2082 | bool accepted) |
| 2083 | { |
| 2084 | const QTransform transform = QQuickItemPrivate::get(item)->windowToItemTransform(); |
| 2085 | |
| 2086 | //create copy of event |
| 2087 | QHoverEvent hoverEvent(type, transform.map(p: scenePos), transform.map(p: lastScenePos), modifiers); |
| 2088 | hoverEvent.setTimestamp(timestamp); |
| 2089 | hoverEvent.setAccepted(accepted); |
| 2090 | |
| 2091 | hasFiltered.clear(); |
| 2092 | if (sendFilteredMouseEvent(event: &hoverEvent, receiver: item, filteringParent: item->parentItem())) |
| 2093 | return true; |
| 2094 | |
| 2095 | QCoreApplication::sendEvent(receiver: item, event: &hoverEvent); |
| 2096 | |
| 2097 | return hoverEvent.isAccepted(); |
| 2098 | } |
| 2099 | |
| 2100 | bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos, |
| 2101 | Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted) |
| 2102 | { |
| 2103 | Q_Q(QQuickWindow); |
| 2104 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
| 2105 | |
| 2106 | if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { |
| 2107 | QPointF p = item->mapFromScene(point: scenePos); |
| 2108 | if (!item->contains(point: p)) |
| 2109 | return false; |
| 2110 | } |
| 2111 | |
| 2112 | qCDebug(DBG_HOVER_TRACE) << q << item << scenePos << lastScenePos << "subtreeHoverEnabled" << itemPrivate->subtreeHoverEnabled; |
| 2113 | if (itemPrivate->subtreeHoverEnabled) { |
| 2114 | QList<QQuickItem *> children = itemPrivate->paintOrderChildItems(); |
| 2115 | for (int ii = children.count() - 1; ii >= 0; --ii) { |
| 2116 | QQuickItem *child = children.at(i: ii); |
| 2117 | if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(item: child)->culled) |
| 2118 | continue; |
| 2119 | if (deliverHoverEvent(item: child, scenePos, lastScenePos, modifiers, timestamp, accepted)) |
| 2120 | return true; |
| 2121 | } |
| 2122 | } |
| 2123 | |
| 2124 | if (itemPrivate->hasPointerHandlers()) { |
| 2125 | QQuickPointerEvent *pointerEvent = pointerEventInstance(device: QQuickPointerDevice::genericMouseDevice(), eventType: QEvent::MouseMove); |
| 2126 | pointerEvent->point(i: 0)->reset(state: Qt::TouchPointMoved, scenePosition: scenePos, pointId: quint64(1) << 24 /* mouse has device ID 1 */, timestamp, velocity: QVector2D()); |
| 2127 | pointerEvent->point(i: 0)->setAccepted(true); |
| 2128 | pointerEvent->localize(target: item); |
| 2129 | for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers) |
| 2130 | if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(object: h)) |
| 2131 | hh->handlePointerEvent(event: pointerEvent); |
| 2132 | } |
| 2133 | |
| 2134 | if (itemPrivate->hoverEnabled) { |
| 2135 | QPointF p = item->mapFromScene(point: scenePos); |
| 2136 | if (item->contains(point: p)) { |
| 2137 | if (!hoverItems.isEmpty() && hoverItems.at(i: 0) == item) { |
| 2138 | //move |
| 2139 | accepted = sendHoverEvent(type: QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted); |
| 2140 | } else { |
| 2141 | QList<QQuickItem *> itemsToHover; |
| 2142 | QQuickItem* parent = item; |
| 2143 | itemsToHover << item; |
| 2144 | while ((parent = parent->parentItem())) |
| 2145 | itemsToHover << parent; |
| 2146 | |
| 2147 | // Leaving from previous hovered items until we reach the item or one of its ancestors. |
| 2148 | while (!hoverItems.isEmpty() && !itemsToHover.contains(t: hoverItems.at(i: 0))) { |
| 2149 | QQuickItem *hoverLeaveItem = hoverItems.takeFirst(); |
| 2150 | sendHoverEvent(type: QEvent::HoverLeave, item: hoverLeaveItem, scenePos, lastScenePos, modifiers, timestamp, accepted); |
| 2151 | QQuickItemPrivate *hoverLeaveItemPrivate = QQuickItemPrivate::get(item: hoverLeaveItem); |
| 2152 | if (hoverLeaveItemPrivate->hasPointerHandlers()) { |
| 2153 | for (QQuickPointerHandler *handler : hoverLeaveItemPrivate->extra->pointerHandlers) { |
| 2154 | if (auto *hh = qmlobject_cast<QQuickHoverHandler *>(object: handler)) { |
| 2155 | QQuickPointerEvent *pointerEvent = pointerEventInstance(device: QQuickPointerDevice::genericMouseDevice(), eventType: QEvent::MouseMove); |
| 2156 | pointerEvent->point(i: 0)->cancelPassiveGrab(handler: hh); |
| 2157 | } |
| 2158 | } |
| 2159 | } |
| 2160 | } |
| 2161 | |
| 2162 | if (!hoverItems.isEmpty() && hoverItems.at(i: 0) == item) {//Not entering a new Item |
| 2163 | // ### Shouldn't we send moves for the parent items as well? |
| 2164 | accepted = sendHoverEvent(type: QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted); |
| 2165 | } else { |
| 2166 | // Enter items that are not entered yet. |
| 2167 | int startIdx = -1; |
| 2168 | if (!hoverItems.isEmpty()) |
| 2169 | startIdx = itemsToHover.indexOf(t: hoverItems.at(i: 0)) - 1; |
| 2170 | if (startIdx == -1) |
| 2171 | startIdx = itemsToHover.count() - 1; |
| 2172 | |
| 2173 | for (int i = startIdx; i >= 0; i--) { |
| 2174 | QQuickItem *itemToHover = itemsToHover.at(i); |
| 2175 | QQuickItemPrivate *itemToHoverPrivate = QQuickItemPrivate::get(item: itemToHover); |
| 2176 | // The item may be about to be deleted or reparented to another window |
| 2177 | // due to another hover event delivered in this function. If that is the |
| 2178 | // case, sending a hover event here will cause a crash or other bad |
| 2179 | // behavior when the leave event is generated. Checking |
| 2180 | // itemToHoverPrivate->window here prevents that case. |
| 2181 | if (itemToHoverPrivate->window == q && itemToHoverPrivate->hoverEnabled) { |
| 2182 | hoverItems.prepend(t: itemToHover); |
| 2183 | sendHoverEvent(type: QEvent::HoverEnter, item: itemToHover, scenePos, lastScenePos, modifiers, timestamp, accepted); |
| 2184 | } |
| 2185 | } |
| 2186 | } |
| 2187 | } |
| 2188 | return true; |
| 2189 | } |
| 2190 | } |
| 2191 | |
| 2192 | return false; |
| 2193 | } |
| 2194 | |
| 2195 | // Simple delivery of non-mouse, non-touch Pointer Events: visit the items and handlers |
| 2196 | // in the usual reverse-paint-order until propagation is stopped |
| 2197 | bool QQuickWindowPrivate::deliverSinglePointEventUntilAccepted(QQuickPointerEvent *event) |
| 2198 | { |
| 2199 | Q_ASSERT(event->pointCount() == 1); |
| 2200 | QQuickEventPoint *point = event->point(i: 0); |
| 2201 | QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point, checkMouseButtons: false, checkAcceptsTouch: false); |
| 2202 | |
| 2203 | for (QQuickItem *item : targetItems) { |
| 2204 | if (!item->window()) |
| 2205 | continue; |
| 2206 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
| 2207 | event->localize(target: item); |
| 2208 | // Let Pointer Handlers have the first shot |
| 2209 | itemPrivate->handlePointerEvent(event); |
| 2210 | if (point->isAccepted()) |
| 2211 | return true; |
| 2212 | QPointF g = item->window()->mapToGlobal(pos: point->scenePosition().toPoint()); |
| 2213 | #if QT_CONFIG(wheelevent) |
| 2214 | // Let the Item have a chance to handle it |
| 2215 | if (QQuickPointerScrollEvent *pse = event->asPointerScrollEvent()) { |
| 2216 | QWheelEvent wheel(point->position(), g, pse->pixelDelta().toPoint(), pse->angleDelta().toPoint(), |
| 2217 | pse->buttons(), pse->modifiers(), pse->phase(), |
| 2218 | pse->isInverted(), pse->synthSource()); |
| 2219 | wheel.setTimestamp(pse->timestamp()); |
| 2220 | wheel.accept(); |
| 2221 | QCoreApplication::sendEvent(receiver: item, event: &wheel); |
| 2222 | if (wheel.isAccepted()) { |
| 2223 | qCDebug(lcWheelTarget) << &wheel << "->" << item; |
| 2224 | event->setAccepted(true); |
| 2225 | return true; |
| 2226 | } |
| 2227 | } |
| 2228 | #endif |
| 2229 | #if QT_CONFIG(gestures) |
| 2230 | if (QQuickPointerNativeGestureEvent *pnge = event->asPointerNativeGestureEvent()) { |
| 2231 | QNativeGestureEvent nge(pnge->type(), pnge->device()->qTouchDevice(), point->position(), point->scenePosition(), g, |
| 2232 | pnge->value(), 0L, 0L); // TODO can't copy things I can't access |
| 2233 | nge.accept(); |
| 2234 | QCoreApplication::sendEvent(receiver: item, event: &nge); |
| 2235 | if (nge.isAccepted()) { |
| 2236 | qCDebug(lcGestureTarget) << &nge << "->" << item; |
| 2237 | event->setAccepted(true); |
| 2238 | return true; |
| 2239 | } |
| 2240 | } |
| 2241 | #endif // gestures |
| 2242 | } |
| 2243 | |
| 2244 | return false; // it wasn't handled |
| 2245 | } |
| 2246 | |
| 2247 | #if QT_CONFIG(wheelevent) |
| 2248 | /*! \reimp */ |
| 2249 | void QQuickWindow::wheelEvent(QWheelEvent *event) |
| 2250 | { |
| 2251 | Q_D(QQuickWindow); |
| 2252 | Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseWheel, |
| 2253 | event->angleDelta().x(), event->angleDelta().y()); |
| 2254 | |
| 2255 | qCDebug(DBG_MOUSE) << "QQuickWindow::wheelEvent()" << event->pixelDelta() << event->angleDelta() << event->phase(); |
| 2256 | |
| 2257 | //if the actual wheel event was accepted, accept the compatibility wheel event and return early |
| 2258 | if (d->lastWheelEventAccepted && event->angleDelta().isNull() && event->phase() == Qt::ScrollUpdate) |
| 2259 | return; |
| 2260 | |
| 2261 | event->ignore(); |
| 2262 | d->deliverPointerEvent(d->pointerEventInstance(ev: event)); |
| 2263 | d->lastWheelEventAccepted = event->isAccepted(); |
| 2264 | } |
| 2265 | #endif // wheelevent |
| 2266 | |
| 2267 | #if QT_CONFIG(tabletevent) |
| 2268 | /*! \reimp */ |
| 2269 | void QQuickWindow::tabletEvent(QTabletEvent *event) |
| 2270 | { |
| 2271 | Q_D(QQuickWindow); |
| 2272 | qCDebug(lcTablet) << event; |
| 2273 | // TODO Qt 6: make sure TabletEnterProximity and TabletLeaveProximity are delivered here |
| 2274 | d->deliverPointerEvent(d->pointerEventInstance(ev: event)); |
| 2275 | } |
| 2276 | #endif // tabletevent |
| 2277 | |
| 2278 | bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) |
| 2279 | { |
| 2280 | qCDebug(DBG_TOUCH) << event; |
| 2281 | Q_Q(QQuickWindow); |
| 2282 | |
| 2283 | if (QQuickItem *grabber = q->mouseGrabberItem()) |
| 2284 | sendUngrabEvent(grabber, touch: true); |
| 2285 | cancelTouchMouseSynthesis(); |
| 2286 | |
| 2287 | // A TouchCancel event will typically not contain any points. |
| 2288 | // Deliver it to all items and handlers that have active touches. |
| 2289 | QQuickPointerEvent *pointerEvent = pointerEventInstance(device: QQuickPointerDevice::touchDevice(d: event->device())); |
| 2290 | for (int i = 0; i < pointerEvent->pointCount(); ++i) |
| 2291 | pointerEvent->point(i)->cancelExclusiveGrabImpl(cancelEvent: event); |
| 2292 | |
| 2293 | // The next touch event can only be a TouchBegin, so clean up. |
| 2294 | pointerEvent->clearGrabbers(); |
| 2295 | return true; |
| 2296 | } |
| 2297 | |
| 2298 | void QQuickWindowPrivate::deliverDelayedTouchEvent() |
| 2299 | { |
| 2300 | // Deliver and delete delayedTouch. |
| 2301 | // Set delayedTouch to 0 before delivery to avoid redelivery in case of |
| 2302 | // event loop recursions (e.g if it the touch starts a dnd session). |
| 2303 | QScopedPointer<QTouchEvent> e(delayedTouch.take()); |
| 2304 | deliverPointerEvent(pointerEventInstance(ev: e.data())); |
| 2305 | } |
| 2306 | |
| 2307 | bool QQuickWindowPrivate::compressTouchEvent(QTouchEvent *event) |
| 2308 | { |
| 2309 | Q_Q(QQuickWindow); |
| 2310 | Qt::TouchPointStates states = event->touchPointStates(); |
| 2311 | if (((states & (Qt::TouchPointMoved | Qt::TouchPointStationary)) == 0) |
| 2312 | || ((states & (Qt::TouchPointPressed | Qt::TouchPointReleased)) != 0)) { |
| 2313 | // we can only compress something that isn't a press or release |
| 2314 | return false; |
| 2315 | } |
| 2316 | |
| 2317 | if (!delayedTouch) { |
| 2318 | delayedTouch.reset(other: new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints())); |
| 2319 | delayedTouch->setTimestamp(event->timestamp()); |
| 2320 | if (renderControl) |
| 2321 | QQuickRenderControlPrivate::get(renderControl)->maybeUpdate(); |
| 2322 | else if (windowManager) |
| 2323 | windowManager->maybeUpdate(window: q); |
| 2324 | return true; |
| 2325 | } |
| 2326 | |
| 2327 | // check if this looks like the last touch event |
| 2328 | if (delayedTouch->type() == event->type() && |
| 2329 | delayedTouch->device() == event->device() && |
| 2330 | delayedTouch->modifiers() == event->modifiers() && |
| 2331 | delayedTouch->touchPoints().count() == event->touchPoints().count()) |
| 2332 | { |
| 2333 | // possible match.. is it really the same? |
| 2334 | bool mismatch = false; |
| 2335 | |
| 2336 | QList<QTouchEvent::TouchPoint> tpts = event->touchPoints(); |
| 2337 | Qt::TouchPointStates states; |
| 2338 | for (int i = 0; i < event->touchPoints().count(); ++i) { |
| 2339 | const QTouchEvent::TouchPoint &tp = tpts.at(i); |
| 2340 | const QTouchEvent::TouchPoint &tpDelayed = delayedTouch->touchPoints().at(i); |
| 2341 | if (tp.id() != tpDelayed.id()) { |
| 2342 | mismatch = true; |
| 2343 | break; |
| 2344 | } |
| 2345 | |
| 2346 | if (tpDelayed.state() == Qt::TouchPointMoved && tp.state() == Qt::TouchPointStationary) |
| 2347 | tpts[i].setState(Qt::TouchPointMoved); |
| 2348 | tpts[i].setLastPos(tpDelayed.lastPos()); |
| 2349 | tpts[i].setLastScenePos(tpDelayed.lastScenePos()); |
| 2350 | tpts[i].setLastScreenPos(tpDelayed.lastScreenPos()); |
| 2351 | tpts[i].setLastNormalizedPos(tpDelayed.lastNormalizedPos()); |
| 2352 | |
| 2353 | states |= tpts.at(i).state(); |
| 2354 | } |
| 2355 | |
| 2356 | // matching touch event? then merge the new event into the old one |
| 2357 | if (!mismatch) { |
| 2358 | delayedTouch->setTouchPoints(tpts); |
| 2359 | delayedTouch->setTimestamp(event->timestamp()); |
| 2360 | return true; |
| 2361 | } |
| 2362 | } |
| 2363 | |
| 2364 | // merging wasn't possible, so deliver the delayed event first, and then delay this one |
| 2365 | deliverDelayedTouchEvent(); |
| 2366 | delayedTouch.reset(other: new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints())); |
| 2367 | delayedTouch->setTimestamp(event->timestamp()); |
| 2368 | return true; |
| 2369 | } |
| 2370 | |
| 2371 | // entry point for touch event delivery: |
| 2372 | // - translate the event to window coordinates |
| 2373 | // - compress the event instead of delivering it if applicable |
| 2374 | // - call deliverTouchPoints to actually dispatch the points |
| 2375 | void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event) |
| 2376 | { |
| 2377 | translateTouchEvent(touchEvent: event); |
| 2378 | if (event->touchPoints().size()) { |
| 2379 | auto point = event->touchPoints().at(i: 0); |
| 2380 | if (point.state() == Qt::TouchPointReleased) { |
| 2381 | lastMousePosition = QPointF(); |
| 2382 | } else { |
| 2383 | lastMousePosition = point.pos(); |
| 2384 | } |
| 2385 | } |
| 2386 | |
| 2387 | qCDebug(DBG_TOUCH) << event; |
| 2388 | |
| 2389 | static bool qquickwindow_no_touch_compression = qEnvironmentVariableIsSet(varName: "QML_NO_TOUCH_COMPRESSION" ); |
| 2390 | |
| 2391 | if (qquickwindow_no_touch_compression || pointerEventRecursionGuard) { |
| 2392 | deliverPointerEvent(pointerEventInstance(ev: event)); |
| 2393 | return; |
| 2394 | } |
| 2395 | |
| 2396 | if (!compressTouchEvent(event)) { |
| 2397 | if (delayedTouch) |
| 2398 | deliverDelayedTouchEvent(); |
| 2399 | deliverPointerEvent(pointerEventInstance(ev: event)); |
| 2400 | } |
| 2401 | } |
| 2402 | |
| 2403 | /*! \reimp */ |
| 2404 | void QQuickWindow::mousePressEvent(QMouseEvent *event) |
| 2405 | { |
| 2406 | Q_D(QQuickWindow); |
| 2407 | d->handleMouseEvent(event); |
| 2408 | } |
| 2409 | /*! \reimp */ |
| 2410 | void QQuickWindow::mouseMoveEvent(QMouseEvent *event) |
| 2411 | { |
| 2412 | Q_D(QQuickWindow); |
| 2413 | d->handleMouseEvent(event); |
| 2414 | } |
| 2415 | /*! \reimp */ |
| 2416 | void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event) |
| 2417 | { |
| 2418 | Q_D(QQuickWindow); |
| 2419 | d->handleMouseEvent(event); |
| 2420 | } |
| 2421 | /*! \reimp */ |
| 2422 | void QQuickWindow::mouseReleaseEvent(QMouseEvent *event) |
| 2423 | { |
| 2424 | Q_D(QQuickWindow); |
| 2425 | d->handleMouseEvent(event); |
| 2426 | } |
| 2427 | |
| 2428 | void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) |
| 2429 | { |
| 2430 | if (event->source() == Qt::MouseEventSynthesizedBySystem) { |
| 2431 | event->accept(); |
| 2432 | return; |
| 2433 | } |
| 2434 | qCDebug(DBG_MOUSE) << "QQuickWindow::handleMouseEvent()" << event->type() << event->localPos() << event->button() << event->buttons(); |
| 2435 | |
| 2436 | switch (event->type()) { |
| 2437 | case QEvent::MouseButtonPress: |
| 2438 | Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMousePress, event->button(), |
| 2439 | event->buttons()); |
| 2440 | deliverPointerEvent(pointerEventInstance(ev: event)); |
| 2441 | break; |
| 2442 | case QEvent::MouseButtonRelease: |
| 2443 | Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(), |
| 2444 | event->buttons()); |
| 2445 | deliverPointerEvent(pointerEventInstance(ev: event)); |
| 2446 | #if QT_CONFIG(cursor) |
| 2447 | updateCursor(scenePos: event->windowPos()); |
| 2448 | #endif |
| 2449 | break; |
| 2450 | case QEvent::MouseButtonDblClick: |
| 2451 | Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick, |
| 2452 | event->button(), event->buttons()); |
| 2453 | if (allowDoubleClick) |
| 2454 | deliverPointerEvent(pointerEventInstance(ev: event)); |
| 2455 | break; |
| 2456 | case QEvent::MouseMove: |
| 2457 | Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove, |
| 2458 | event->localPos().x(), event->localPos().y()); |
| 2459 | |
| 2460 | qCDebug(DBG_HOVER_TRACE) << this; |
| 2461 | |
| 2462 | #if QT_CONFIG(cursor) |
| 2463 | updateCursor(scenePos: event->windowPos()); |
| 2464 | #endif |
| 2465 | |
| 2466 | if (!pointerEventInstance(device: QQuickPointerDevice::genericMouseDevice())->point(i: 0)->exclusiveGrabber()) { |
| 2467 | QPointF last = lastMousePosition.isNull() ? event->windowPos() : lastMousePosition; |
| 2468 | lastMousePosition = event->windowPos(); |
| 2469 | |
| 2470 | bool accepted = event->isAccepted(); |
| 2471 | bool delivered = deliverHoverEvent(item: contentItem, scenePos: event->windowPos(), lastScenePos: last, modifiers: event->modifiers(), timestamp: event->timestamp(), accepted); |
| 2472 | if (!delivered) { |
| 2473 | //take care of any exits |
| 2474 | accepted = clearHover(timestamp: event->timestamp()); |
| 2475 | } |
| 2476 | event->setAccepted(accepted); |
| 2477 | } |
| 2478 | deliverPointerEvent(pointerEventInstance(ev: event)); |
| 2479 | break; |
| 2480 | default: |
| 2481 | Q_ASSERT(false); |
| 2482 | break; |
| 2483 | } |
| 2484 | } |
| 2485 | |
| 2486 | void QQuickWindowPrivate::flushFrameSynchronousEvents() |
| 2487 | { |
| 2488 | Q_Q(QQuickWindow); |
| 2489 | |
| 2490 | if (delayedTouch) { |
| 2491 | deliverDelayedTouchEvent(); |
| 2492 | |
| 2493 | // Touch events which constantly start animations (such as a behavior tracking |
| 2494 | // the mouse point) need animations to start. |
| 2495 | QQmlAnimationTimer *ut = QQmlAnimationTimer::instance(); |
| 2496 | if (ut && ut->hasStartAnimationPending()) |
| 2497 | ut->startAnimations(); |
| 2498 | } |
| 2499 | |
| 2500 | // In webOS we already have the alternative to the issue that this |
| 2501 | // wanted to address and thus skipping this part won't break anything. |
| 2502 | #if !defined(Q_OS_WEBOS) |
| 2503 | // Once per frame, if any items are dirty, send a synthetic hover, |
| 2504 | // in case items have changed position, visibility, etc. |
| 2505 | // For instance, during animation (including the case of a ListView |
| 2506 | // whose delegates contain MouseAreas), a MouseArea needs to know |
| 2507 | // whether it has moved into a position where it is now under the cursor. |
| 2508 | if (!q->mouseGrabberItem() && !lastMousePosition.isNull() && dirtyItemList) { |
| 2509 | bool accepted = false; |
| 2510 | bool delivered = deliverHoverEvent(item: contentItem, scenePos: lastMousePosition, lastScenePos: lastMousePosition, modifiers: QGuiApplication::keyboardModifiers(), timestamp: 0, accepted); |
| 2511 | if (!delivered) |
| 2512 | clearHover(); // take care of any exits |
| 2513 | } |
| 2514 | #endif |
| 2515 | } |
| 2516 | |
| 2517 | QQuickPointerEvent *QQuickWindowPrivate::queryPointerEventInstance(QQuickPointerDevice *device, QEvent::Type eventType) const |
| 2518 | { |
| 2519 | // Search for a matching reusable event object. |
| 2520 | for (QQuickPointerEvent *e : pointerEventInstances) { |
| 2521 | // If device can generate native gestures (e.g. a trackpad), there might be multiple QQuickPointerEvents: |
| 2522 | // QQuickPointerNativeGestureEvent, QQuickPointerScrollEvent, and QQuickPointerTouchEvent. |
| 2523 | // Use eventType to disambiguate. |
| 2524 | #if QT_CONFIG(gestures) |
| 2525 | if ((eventType == QEvent::NativeGesture) != bool(e->asPointerNativeGestureEvent())) |
| 2526 | continue; |
| 2527 | #endif |
| 2528 | if ((eventType == QEvent::Wheel) != bool(e->asPointerScrollEvent())) |
| 2529 | continue; |
| 2530 | // Otherwise we assume there's only one event type per device. |
| 2531 | // More disambiguation tests might need to be added above if that changes later. |
| 2532 | if (e->device() == device) |
| 2533 | return e; |
| 2534 | } |
| 2535 | return nullptr; |
| 2536 | } |
| 2537 | |
| 2538 | QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QQuickPointerDevice *device, QEvent::Type eventType) const |
| 2539 | { |
| 2540 | QQuickPointerEvent *ev = queryPointerEventInstance(device, eventType); |
| 2541 | if (ev) |
| 2542 | return ev; |
| 2543 | QQuickWindow *q = const_cast<QQuickWindow*>(q_func()); |
| 2544 | switch (device->type()) { |
| 2545 | case QQuickPointerDevice::Mouse: |
| 2546 | // QWindowSystemInterface::handleMouseEvent() does not take a device parameter: |
| 2547 | // we assume all mouse events come from one mouse (the "core pointer"). |
| 2548 | // So when the event is a mouse event, device == QQuickPointerDevice::genericMouseDevice() |
| 2549 | if (eventType == QEvent::Wheel) |
| 2550 | ev = new QQuickPointerScrollEvent(q, device); |
| 2551 | else |
| 2552 | ev = new QQuickPointerMouseEvent(q, device); |
| 2553 | break; |
| 2554 | case QQuickPointerDevice::TouchPad: |
| 2555 | case QQuickPointerDevice::TouchScreen: |
| 2556 | #if QT_CONFIG(gestures) |
| 2557 | if (eventType == QEvent::NativeGesture) |
| 2558 | ev = new QQuickPointerNativeGestureEvent(q, device); |
| 2559 | else // assume QEvent::Type is one of TouchBegin/Update/End |
| 2560 | #endif |
| 2561 | ev = new QQuickPointerTouchEvent(q, device); |
| 2562 | break; |
| 2563 | #if QT_CONFIG(tabletevent) |
| 2564 | case QQuickPointerDevice::Stylus: |
| 2565 | case QQuickPointerDevice::Airbrush: |
| 2566 | case QQuickPointerDevice::Puck: |
| 2567 | ev = new QQuickPointerTabletEvent(q, device); |
| 2568 | break; |
| 2569 | #endif |
| 2570 | default: |
| 2571 | break; |
| 2572 | } |
| 2573 | pointerEventInstances << ev; |
| 2574 | return ev; |
| 2575 | } |
| 2576 | |
| 2577 | /*! |
| 2578 | \internal |
| 2579 | Returns a QQuickPointerEvent instance suitable for wrapping and delivering \a event. |
| 2580 | |
| 2581 | There is a unique instance per QQuickPointerDevice, which is determined |
| 2582 | from \a event's device. |
| 2583 | */ |
| 2584 | QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QEvent *event) const |
| 2585 | { |
| 2586 | QQuickPointerDevice *dev = nullptr; |
| 2587 | switch (event->type()) { |
| 2588 | case QEvent::MouseButtonPress: |
| 2589 | case QEvent::MouseButtonRelease: |
| 2590 | case QEvent::MouseButtonDblClick: |
| 2591 | case QEvent::MouseMove: |
| 2592 | case QEvent::Wheel: |
| 2593 | dev = QQuickPointerDevice::genericMouseDevice(); |
| 2594 | break; |
| 2595 | case QEvent::TouchBegin: |
| 2596 | case QEvent::TouchUpdate: |
| 2597 | case QEvent::TouchEnd: |
| 2598 | case QEvent::TouchCancel: |
| 2599 | dev = QQuickPointerDevice::touchDevice(d: static_cast<QTouchEvent *>(event)->device()); |
| 2600 | break; |
| 2601 | #if QT_CONFIG(tabletevent) |
| 2602 | case QEvent::TabletPress: |
| 2603 | case QEvent::TabletMove: |
| 2604 | case QEvent::TabletRelease: |
| 2605 | case QEvent::TabletEnterProximity: |
| 2606 | case QEvent::TabletLeaveProximity: |
| 2607 | dev = QQuickPointerDevice::tabletDevice(event: static_cast<QTabletEvent *>(event)); |
| 2608 | break; |
| 2609 | #endif |
| 2610 | #if QT_CONFIG(gestures) |
| 2611 | case QEvent::NativeGesture: |
| 2612 | dev = QQuickPointerDevice::touchDevice(d: static_cast<QNativeGestureEvent *>(event)->device()); |
| 2613 | break; |
| 2614 | #endif |
| 2615 | default: |
| 2616 | break; |
| 2617 | } |
| 2618 | |
| 2619 | Q_ASSERT(dev); |
| 2620 | auto pev = pointerEventInstance(device: dev, eventType: event->type()); |
| 2621 | Q_ASSERT(pev); |
| 2622 | return pev->reset(ev: event); |
| 2623 | } |
| 2624 | |
| 2625 | void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) |
| 2626 | { |
| 2627 | Q_Q(QQuickWindow); |
| 2628 | // If users spin the eventloop as a result of event delivery, we disable |
| 2629 | // event compression and send events directly. This is because we consider |
| 2630 | // the usecase a bit evil, but we at least don't want to lose events. |
| 2631 | ++pointerEventRecursionGuard; |
| 2632 | |
| 2633 | skipDelivery.clear(); |
| 2634 | if (event->asPointerMouseEvent()) { |
| 2635 | deliverMouseEvent(pointerEvent: event->asPointerMouseEvent()); |
| 2636 | // failsafe: never allow any kind of grab to persist after release |
| 2637 | if (event->isReleaseEvent() && event->buttons() == Qt::NoButton) { |
| 2638 | QQuickItem *oldGrabber = q->mouseGrabberItem(); |
| 2639 | event->clearGrabbers(); |
| 2640 | sendUngrabEvent(grabber: oldGrabber, touch: false); |
| 2641 | } |
| 2642 | } else if (event->asPointerTouchEvent()) { |
| 2643 | deliverTouchEvent(event->asPointerTouchEvent()); |
| 2644 | } else { |
| 2645 | deliverSinglePointEventUntilAccepted(event); |
| 2646 | // If any handler got interested in the tablet event, we don't want to receive a synth-mouse event from QtGui |
| 2647 | // TODO Qt 6: QTabletEvent will be accepted by default, like other events |
| 2648 | if (event->asPointerTabletEvent() && |
| 2649 | (!event->point(i: 0)->passiveGrabbers().isEmpty() || event->point(i: 0)->exclusiveGrabber())) |
| 2650 | event->setAccepted(true); |
| 2651 | } |
| 2652 | |
| 2653 | event->reset(ev: nullptr); |
| 2654 | |
| 2655 | --pointerEventRecursionGuard; |
| 2656 | } |
| 2657 | |
| 2658 | // check if item or any of its child items contain the point, or if any pointer handler "wants" the point |
| 2659 | // FIXME: should this be iterative instead of recursive? |
| 2660 | // If checkMouseButtons is true, it means we are finding targets for a mouse event, so no item for which acceptedMouseButtons() is NoButton will be added. |
| 2661 | // If checkAcceptsTouch is true, it means we are finding targets for a touch event, so either acceptTouchEvents() must return true OR |
| 2662 | // it must accept a synth. mouse event, thus if acceptTouchEvents() returns false but acceptedMouseButtons() is true, gets added; if not, it doesn't. |
| 2663 | QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, QQuickEventPoint *point, bool checkMouseButtons, bool checkAcceptsTouch) const |
| 2664 | { |
| 2665 | QVector<QQuickItem *> targets; |
| 2666 | auto itemPrivate = QQuickItemPrivate::get(item); |
| 2667 | QPointF itemPos = item->mapFromScene(point: point->scenePosition()); |
| 2668 | // if the item clips, we can potentially return early |
| 2669 | if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { |
| 2670 | if (!item->contains(point: itemPos)) |
| 2671 | return targets; |
| 2672 | } |
| 2673 | |
| 2674 | // recurse for children |
| 2675 | QList<QQuickItem *> children = itemPrivate->paintOrderChildItems(); |
| 2676 | for (int ii = children.count() - 1; ii >= 0; --ii) { |
| 2677 | QQuickItem *child = children.at(i: ii); |
| 2678 | auto childPrivate = QQuickItemPrivate::get(item: child); |
| 2679 | if (!child->isVisible() || !child->isEnabled() || childPrivate->culled) |
| 2680 | continue; |
| 2681 | targets << pointerTargets(item: child, point, checkMouseButtons, checkAcceptsTouch); |
| 2682 | } |
| 2683 | |
| 2684 | bool relevant = item->contains(point: itemPos); |
| 2685 | if (itemPrivate->hasPointerHandlers()) { |
| 2686 | if (!relevant) |
| 2687 | if (itemPrivate->anyPointerHandlerWants(point)) |
| 2688 | relevant = true; |
| 2689 | } else { |
| 2690 | if (relevant && checkMouseButtons && item->acceptedMouseButtons() == Qt::NoButton) |
| 2691 | relevant = false; |
| 2692 | if (relevant && checkAcceptsTouch && !(item->acceptTouchEvents() || item->acceptedMouseButtons())) |
| 2693 | relevant = false; |
| 2694 | } |
| 2695 | if (relevant) |
| 2696 | targets << item; // add this item last: children take precedence |
| 2697 | return targets; |
| 2698 | } |
| 2699 | |
| 2700 | // return the joined lists |
| 2701 | // list1 has priority, common items come last |
| 2702 | QVector<QQuickItem *> QQuickWindowPrivate::mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const |
| 2703 | { |
| 2704 | QVector<QQuickItem *> targets = list1; |
| 2705 | // start at the end of list2 |
| 2706 | // if item not in list, append it |
| 2707 | // if item found, move to next one, inserting before the last found one |
| 2708 | int insertPosition = targets.length(); |
| 2709 | for (int i = list2.length() - 1; i >= 0; --i) { |
| 2710 | int newInsertPosition = targets.lastIndexOf(t: list2.at(i), from: insertPosition); |
| 2711 | if (newInsertPosition >= 0) { |
| 2712 | Q_ASSERT(newInsertPosition <= insertPosition); |
| 2713 | insertPosition = newInsertPosition; |
| 2714 | } |
| 2715 | // check for duplicates, only insert if the item isn't there already |
| 2716 | if (insertPosition == targets.size() || list2.at(i) != targets.at(i: insertPosition)) |
| 2717 | targets.insert(i: insertPosition, t: list2.at(i)); |
| 2718 | } |
| 2719 | return targets; |
| 2720 | } |
| 2721 | |
| 2722 | void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) |
| 2723 | { |
| 2724 | qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent(); |
| 2725 | |
| 2726 | if (event->isPressEvent()) |
| 2727 | deliverPressOrReleaseEvent(event); |
| 2728 | if (!event->allUpdatedPointsAccepted()) |
| 2729 | deliverUpdatedTouchPoints(event); |
| 2730 | if (event->isReleaseEvent()) |
| 2731 | deliverPressOrReleaseEvent(event, handlersOnly: true); |
| 2732 | |
| 2733 | // Remove released points from itemForTouchPointId |
| 2734 | bool allReleased = true; |
| 2735 | int pointCount = event->pointCount(); |
| 2736 | for (int i = 0; i < pointCount; ++i) { |
| 2737 | QQuickEventPoint *point = event->point(i); |
| 2738 | if (point->state() == QQuickEventPoint::Released) { |
| 2739 | int id = point->pointId(); |
| 2740 | qCDebug(DBG_TOUCH_TARGET) << "TP" << Qt::hex << id << "released" ; |
| 2741 | point->setGrabberItem(nullptr); |
| 2742 | if (id == touchMouseId) |
| 2743 | cancelTouchMouseSynthesis(); |
| 2744 | } else { |
| 2745 | allReleased = false; |
| 2746 | } |
| 2747 | } |
| 2748 | |
| 2749 | if (allReleased) { |
| 2750 | if (Q_UNLIKELY(!event->exclusiveGrabbers().isEmpty())) |
| 2751 | qWarning() << "No release received for some grabbers" << event->exclusiveGrabbers(); |
| 2752 | event->clearGrabbers(); |
| 2753 | } |
| 2754 | } |
| 2755 | |
| 2756 | // Deliver touch points to existing grabbers |
| 2757 | void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event) |
| 2758 | { |
| 2759 | bool done = false; |
| 2760 | const auto grabbers = event->exclusiveGrabbers(); |
| 2761 | for (auto grabber : grabbers) { |
| 2762 | // The grabber is guaranteed to be either an item or a handler. |
| 2763 | QQuickItem *receiver = qmlobject_cast<QQuickItem *>(object: grabber); |
| 2764 | if (!receiver) { |
| 2765 | // The grabber is not an item? It's a handler then. Let it have the event first. |
| 2766 | QQuickPointerHandler *handler = static_cast<QQuickPointerHandler *>(grabber); |
| 2767 | receiver = static_cast<QQuickPointerHandler *>(grabber)->parentItem(); |
| 2768 | hasFiltered.clear(); |
| 2769 | if (sendFilteredPointerEvent(event, receiver)) |
| 2770 | done = true; |
| 2771 | event->localize(target: receiver); |
| 2772 | handler->handlePointerEvent(event); |
| 2773 | } |
| 2774 | if (done) |
| 2775 | break; |
| 2776 | // If the grabber is an item or the grabbing handler didn't handle it, |
| 2777 | // then deliver the event to the item (which may have multiple handlers). |
| 2778 | deliverMatchingPointsToItem(item: receiver, pointerEvent: event); |
| 2779 | } |
| 2780 | |
| 2781 | // Deliver to each eventpoint's passive grabbers (but don't visit any handler more than once) |
| 2782 | int pointCount = event->pointCount(); |
| 2783 | for (int i = 0; i < pointCount; ++i) { |
| 2784 | QQuickEventPoint *point = event->point(i); |
| 2785 | deliverToPassiveGrabbers(passiveGrabbers: point->passiveGrabbers(), pointerEvent: event); |
| 2786 | } |
| 2787 | |
| 2788 | if (done) |
| 2789 | return; |
| 2790 | |
| 2791 | // If some points weren't grabbed, deliver only to non-grabber PointerHandlers in reverse paint order |
| 2792 | if (!event->allPointsGrabbed()) { |
| 2793 | QVector<QQuickItem *> targetItems; |
| 2794 | for (int i = 0; i < pointCount; ++i) { |
| 2795 | QQuickEventPoint *point = event->point(i); |
| 2796 | if (point->state() == QQuickEventPoint::Pressed) |
| 2797 | continue; // presses were delivered earlier; not the responsibility of deliverUpdatedTouchPoints |
| 2798 | QVector<QQuickItem *> targetItemsForPoint = pointerTargets(item: contentItem, point, checkMouseButtons: false, checkAcceptsTouch: false); |
| 2799 | if (targetItems.count()) { |
| 2800 | targetItems = mergePointerTargets(list1: targetItems, list2: targetItemsForPoint); |
| 2801 | } else { |
| 2802 | targetItems = targetItemsForPoint; |
| 2803 | } |
| 2804 | } |
| 2805 | for (QQuickItem *item : targetItems) { |
| 2806 | if (grabbers.contains(t: item)) |
| 2807 | continue; |
| 2808 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
| 2809 | event->localize(target: item); |
| 2810 | itemPrivate->handlePointerEvent(event, avoidExclusiveGrabber: true); // avoid re-delivering to grabbers |
| 2811 | if (event->allPointsGrabbed()) |
| 2812 | break; |
| 2813 | } |
| 2814 | } |
| 2815 | } |
| 2816 | |
| 2817 | // Deliver an event containing newly pressed or released touch points |
| 2818 | bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event, bool handlersOnly) |
| 2819 | { |
| 2820 | int pointCount = event->pointCount(); |
| 2821 | QVector<QQuickItem *> targetItems; |
| 2822 | bool isTouchEvent = (event->asPointerTouchEvent() != nullptr); |
| 2823 | if (isTouchEvent && event->isPressEvent() && isDeliveringTouchAsMouse()) { |
| 2824 | if (const QQuickEventPoint *point = pointerEventInstance(device: touchMouseDevice)->pointById(pointId: touchMouseId)) { |
| 2825 | // When a second point is pressed, if the first point's existing |
| 2826 | // grabber was a pointer handler while a filtering parent is filtering |
| 2827 | // the same first point _as mouse_: we're starting over with delivery, |
| 2828 | // so we need to allow the second point to now be sent as a synth-mouse |
| 2829 | // instead of the first one, so that filtering parents (maybe even the |
| 2830 | // same one) can get a chance to see the second touchpoint as a |
| 2831 | // synth-mouse and perhaps grab it. Ideally we would always do this |
| 2832 | // when a new touchpoint is pressed, but this compromise fixes |
| 2833 | // QTBUG-70998 and avoids breaking tst_FlickableInterop::touchDragSliderAndFlickable |
| 2834 | if (point->grabberPointerHandler()) |
| 2835 | cancelTouchMouseSynthesis(); |
| 2836 | } else { |
| 2837 | qCWarning(DBG_TOUCH_TARGET) << "during delivery of touch press, synth-mouse ID" << Qt::hex << touchMouseId << "is missing from" << event; |
| 2838 | } |
| 2839 | } |
| 2840 | for (int i = 0; i < pointCount; ++i) { |
| 2841 | auto point = event->point(i); |
| 2842 | if (point->state() == QQuickEventPoint::Pressed && !event->isDoubleClickEvent()) |
| 2843 | point->clearPassiveGrabbers(); |
| 2844 | point->setAccepted(false); // because otherwise touchEventForItem will ignore it |
| 2845 | if (point->grabberPointerHandler() && point->state() == QQuickEventPoint::Released) |
| 2846 | point->setGrabberPointerHandler(exclusiveGrabber: nullptr, exclusive: true); |
| 2847 | QVector<QQuickItem *> targetItemsForPoint = pointerTargets(item: contentItem, point, checkMouseButtons: !isTouchEvent, checkAcceptsTouch: isTouchEvent); |
| 2848 | if (targetItems.count()) { |
| 2849 | targetItems = mergePointerTargets(list1: targetItems, list2: targetItemsForPoint); |
| 2850 | } else { |
| 2851 | targetItems = targetItemsForPoint; |
| 2852 | } |
| 2853 | } |
| 2854 | |
| 2855 | for (QQuickItem *item : targetItems) { |
| 2856 | if (!event->m_event) { |
| 2857 | qWarning(msg: "event went missing during delivery! (nested sendEvent() is not allowed)" ); |
| 2858 | break; |
| 2859 | } |
| 2860 | hasFiltered.clear(); |
| 2861 | if (!handlersOnly && sendFilteredPointerEvent(event, receiver: item)) { |
| 2862 | if (event->isAccepted()) { |
| 2863 | for (int i = 0; i < event->pointCount(); ++i) |
| 2864 | event->point(i)->setAccepted(); |
| 2865 | return true; |
| 2866 | } |
| 2867 | skipDelivery.append(t: item); |
| 2868 | } |
| 2869 | |
| 2870 | // Do not deliverMatchingPointsTo any item for which the filtering parent already intercepted the event, |
| 2871 | // nor to any item which already had a chance to filter. |
| 2872 | if (skipDelivery.contains(t: item)) |
| 2873 | continue; |
| 2874 | if (!event->m_event) { |
| 2875 | qWarning(msg: "event went missing during delivery! (nested sendEvent() is not allowed)" ); |
| 2876 | break; |
| 2877 | } |
| 2878 | deliverMatchingPointsToItem(item, pointerEvent: event, handlersOnly); |
| 2879 | if (event->allPointsAccepted()) |
| 2880 | handlersOnly = true; |
| 2881 | } |
| 2882 | |
| 2883 | return event->allPointsAccepted(); |
| 2884 | } |
| 2885 | |
| 2886 | void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly) |
| 2887 | { |
| 2888 | Q_Q(QQuickWindow); |
| 2889 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
| 2890 | #if defined(Q_OS_ANDROID) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |
| 2891 | // QTBUG-85379 |
| 2892 | // In QT_VERSION below 6.0.0 touchEnabled for QtQuickItems is set by default to true |
| 2893 | // It causes delivering touch events to Items which are not interested |
| 2894 | // In some cases (like using Material Style in Android) it may cause a crash |
| 2895 | if (itemPrivate->wasDeleted) |
| 2896 | return; |
| 2897 | #endif |
| 2898 | pointerEvent->localize(target: item); |
| 2899 | |
| 2900 | // Let the Item's handlers (if any) have the event first. |
| 2901 | // However, double click should never be delivered to handlers. |
| 2902 | if (!pointerEvent->isDoubleClickEvent()) { |
| 2903 | bool wasAccepted = pointerEvent->allPointsAccepted(); |
| 2904 | itemPrivate->handlePointerEvent(pointerEvent); |
| 2905 | allowDoubleClick = wasAccepted || !(pointerEvent->asPointerMouseEvent() && pointerEvent->isPressEvent() && pointerEvent->allPointsAccepted()); |
| 2906 | } |
| 2907 | if (handlersOnly) |
| 2908 | return; |
| 2909 | |
| 2910 | // If all points are released and the item is not the grabber, it doesn't get the event. |
| 2911 | // But if at least one point is still pressed, we might be in a potential gesture-takeover scenario. |
| 2912 | if (pointerEvent->isReleaseEvent() && !pointerEvent->isUpdateEvent() |
| 2913 | && !pointerEvent->exclusiveGrabbers().contains(t: item)) |
| 2914 | return; |
| 2915 | |
| 2916 | // TODO: unite this mouse point delivery with the synthetic mouse event below |
| 2917 | auto event = pointerEvent->asPointerMouseEvent(); |
| 2918 | if (event && item->acceptedMouseButtons() & event->button()) { |
| 2919 | auto point = event->point(i: 0); |
| 2920 | // The only reason to already have a mouse grabber here is |
| 2921 | // synthetic events - flickable sends one when setPressDelay is used. |
| 2922 | auto oldMouseGrabber = q->mouseGrabberItem(); |
| 2923 | QPointF localPos = item->mapFromScene(point: point->scenePosition()); |
| 2924 | QMouseEvent *me = event->asMouseEvent(localPos); |
| 2925 | me->accept(); |
| 2926 | QCoreApplication::sendEvent(receiver: item, event: me); |
| 2927 | if (me->isAccepted()) { |
| 2928 | auto mouseGrabber = q->mouseGrabberItem(); |
| 2929 | if (mouseGrabber && mouseGrabber != item && mouseGrabber != oldMouseGrabber) { |
| 2930 | item->mouseUngrabEvent(); |
| 2931 | } else if (item->isEnabled() && item->isVisible()) { |
| 2932 | item->grabMouse(); |
| 2933 | } |
| 2934 | point->setAccepted(true); |
| 2935 | } |
| 2936 | return; |
| 2937 | } |
| 2938 | |
| 2939 | QQuickPointerTouchEvent *ptEvent = pointerEvent->asPointerTouchEvent(); |
| 2940 | if (!ptEvent) |
| 2941 | return; |
| 2942 | |
| 2943 | QScopedPointer<QTouchEvent> touchEvent(ptEvent->touchEventForItem(item)); |
| 2944 | if (!touchEvent) |
| 2945 | return; |
| 2946 | |
| 2947 | qCDebug(DBG_TOUCH) << "considering delivering " << touchEvent.data() << " to " << item; |
| 2948 | bool eventAccepted = false; |
| 2949 | |
| 2950 | // If any parent filters the event, we're done. |
| 2951 | hasFiltered.clear(); |
| 2952 | if (sendFilteredPointerEvent(event: pointerEvent, receiver: item)) |
| 2953 | return; |
| 2954 | |
| 2955 | // Deliver the touch event to the given item |
| 2956 | qCDebug(DBG_TOUCH) << " - actually delivering " << touchEvent.data() << " to " << item; |
| 2957 | QCoreApplication::sendEvent(receiver: item, event: touchEvent.data()); |
| 2958 | eventAccepted = touchEvent->isAccepted(); |
| 2959 | |
| 2960 | // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it. |
| 2961 | if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) { |
| 2962 | if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) { |
| 2963 | // send mouse event |
| 2964 | if (deliverTouchAsMouse(item, pointerEvent: ptEvent)) |
| 2965 | eventAccepted = true; |
| 2966 | } |
| 2967 | } |
| 2968 | |
| 2969 | if (eventAccepted) { |
| 2970 | // If the touch was accepted (regardless by whom or in what form), |
| 2971 | // update accepted new points. |
| 2972 | bool isPressOrRelease = pointerEvent->isPressEvent() || pointerEvent->isReleaseEvent(); |
| 2973 | for (const auto &point: qAsConst(t: touchEvent->touchPoints())) { |
| 2974 | if (auto pointerEventPoint = ptEvent->pointById(pointId: point.id())) { |
| 2975 | pointerEventPoint->setAccepted(); |
| 2976 | if (isPressOrRelease) |
| 2977 | pointerEventPoint->setGrabberItem(item); |
| 2978 | } |
| 2979 | } |
| 2980 | } else { |
| 2981 | // But if the event was not accepted then we know this item |
| 2982 | // will not be interested in further updates for those touchpoint IDs either. |
| 2983 | for (const auto &point: qAsConst(t: touchEvent->touchPoints())) { |
| 2984 | if (point.state() == Qt::TouchPointPressed) { |
| 2985 | if (auto *tp = ptEvent->pointById(pointId: point.id())) { |
| 2986 | if (tp->exclusiveGrabber() == item) { |
| 2987 | qCDebug(DBG_TOUCH_TARGET) << "TP" << Qt::hex << point.id() << "disassociated" ; |
| 2988 | tp->setGrabberItem(nullptr); |
| 2989 | } |
| 2990 | } |
| 2991 | } |
| 2992 | } |
| 2993 | } |
| 2994 | } |
| 2995 | |
| 2996 | #if QT_CONFIG(quick_draganddrop) |
| 2997 | void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event) |
| 2998 | { |
| 2999 | grabber->resetTarget(); |
| 3000 | QQuickDragGrabber::iterator grabItem = grabber->begin(); |
| 3001 | if (grabItem != grabber->end()) { |
| 3002 | Q_ASSERT(event->type() != QEvent::DragEnter); |
| 3003 | if (event->type() == QEvent::Drop) { |
| 3004 | QDropEvent *e = static_cast<QDropEvent *>(event); |
| 3005 | for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(at: grabItem)) { |
| 3006 | QPointF p = (**grabItem)->mapFromScene(point: e->pos()); |
| 3007 | QDropEvent translatedEvent( |
| 3008 | p.toPoint(), |
| 3009 | e->possibleActions(), |
| 3010 | e->mimeData(), |
| 3011 | e->mouseButtons(), |
| 3012 | e->keyboardModifiers()); |
| 3013 | QQuickDropEventEx::copyActions(to: &translatedEvent, from: *e); |
| 3014 | QCoreApplication::sendEvent(receiver: **grabItem, event: &translatedEvent); |
| 3015 | e->setAccepted(translatedEvent.isAccepted()); |
| 3016 | e->setDropAction(translatedEvent.dropAction()); |
| 3017 | grabber->setTarget(**grabItem); |
| 3018 | } |
| 3019 | } |
| 3020 | if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave. |
| 3021 | QDragLeaveEvent leaveEvent; |
| 3022 | for (; grabItem != grabber->end(); grabItem = grabber->release(at: grabItem)) |
| 3023 | QCoreApplication::sendEvent(receiver: **grabItem, event: &leaveEvent); |
| 3024 | return; |
| 3025 | } else { |
| 3026 | QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event); |
| 3027 | |
| 3028 | // Used to ensure we don't send DragEnterEvents to current drop targets, |
| 3029 | // and to detect which current drop targets we have left |
| 3030 | QVarLengthArray<QQuickItem*, 64> currentGrabItems; |
| 3031 | for (; grabItem != grabber->end(); grabItem = grabber->release(at: grabItem)) |
| 3032 | currentGrabItems.append(t: **grabItem); |
| 3033 | |
| 3034 | // Look for any other potential drop targets that are higher than the current ones |
| 3035 | QDragEnterEvent enterEvent( |
| 3036 | moveEvent->pos(), |
| 3037 | moveEvent->possibleActions(), |
| 3038 | moveEvent->mimeData(), |
| 3039 | moveEvent->mouseButtons(), |
| 3040 | moveEvent->keyboardModifiers()); |
| 3041 | QQuickDropEventEx::copyActions(to: &enterEvent, from: *moveEvent); |
| 3042 | event->setAccepted(deliverDragEvent(grabber, contentItem, &enterEvent, currentGrabItems: ¤tGrabItems)); |
| 3043 | |
| 3044 | for (grabItem = grabber->begin(); grabItem != grabber->end(); ++grabItem) { |
| 3045 | int i = currentGrabItems.indexOf(t: **grabItem); |
| 3046 | if (i >= 0) { |
| 3047 | currentGrabItems.remove(i); |
| 3048 | // Still grabbed: send move event |
| 3049 | QDragMoveEvent translatedEvent( |
| 3050 | (**grabItem)->mapFromScene(point: moveEvent->pos()).toPoint(), |
| 3051 | moveEvent->possibleActions(), |
| 3052 | moveEvent->mimeData(), |
| 3053 | moveEvent->mouseButtons(), |
| 3054 | moveEvent->keyboardModifiers()); |
| 3055 | QQuickDropEventEx::copyActions(to: &translatedEvent, from: *moveEvent); |
| 3056 | QCoreApplication::sendEvent(receiver: **grabItem, event: &translatedEvent); |
| 3057 | event->setAccepted(translatedEvent.isAccepted()); |
| 3058 | QQuickDropEventEx::copyActions(to: moveEvent, from: translatedEvent); |
| 3059 | } |
| 3060 | } |
| 3061 | |
| 3062 | // Anything left in currentGrabItems is no longer a drop target and should be sent a DragLeaveEvent |
| 3063 | QDragLeaveEvent leaveEvent; |
| 3064 | for (QQuickItem *i : currentGrabItems) |
| 3065 | QCoreApplication::sendEvent(receiver: i, event: &leaveEvent); |
| 3066 | |
| 3067 | return; |
| 3068 | } |
| 3069 | } |
| 3070 | if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) { |
| 3071 | QDragMoveEvent *e = static_cast<QDragMoveEvent *>(event); |
| 3072 | QDragEnterEvent enterEvent( |
| 3073 | e->pos(), |
| 3074 | e->possibleActions(), |
| 3075 | e->mimeData(), |
| 3076 | e->mouseButtons(), |
| 3077 | e->keyboardModifiers()); |
| 3078 | QQuickDropEventEx::copyActions(to: &enterEvent, from: *e); |
| 3079 | event->setAccepted(deliverDragEvent(grabber, contentItem, &enterEvent)); |
| 3080 | } |
| 3081 | } |
| 3082 | |
| 3083 | bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event, QVarLengthArray<QQuickItem*, 64> *currentGrabItems) |
| 3084 | { |
| 3085 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
| 3086 | if (!item->isVisible() || !item->isEnabled() || QQuickItemPrivate::get(item)->culled) |
| 3087 | return false; |
| 3088 | QPointF p = item->mapFromScene(point: event->pos()); |
| 3089 | bool itemContained = item->contains(point: p); |
| 3090 | |
| 3091 | if (!itemContained && itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { |
| 3092 | return false; |
| 3093 | } |
| 3094 | |
| 3095 | QDragEnterEvent enterEvent( |
| 3096 | event->pos(), |
| 3097 | event->possibleActions(), |
| 3098 | event->mimeData(), |
| 3099 | event->mouseButtons(), |
| 3100 | event->keyboardModifiers()); |
| 3101 | QQuickDropEventEx::copyActions(to: &enterEvent, from: *event); |
| 3102 | QList<QQuickItem *> children = itemPrivate->paintOrderChildItems(); |
| 3103 | |
| 3104 | // Check children in front of this item first |
| 3105 | for (int ii = children.count() - 1; ii >= 0; --ii) { |
| 3106 | if (children.at(i: ii)->z() < 0) |
| 3107 | continue; |
| 3108 | if (deliverDragEvent(grabber, item: children.at(i: ii), event: &enterEvent, currentGrabItems)) |
| 3109 | return true; |
| 3110 | } |
| 3111 | |
| 3112 | if (itemContained) { |
| 3113 | // If this item is currently grabbed, don't send it another DragEnter, |
| 3114 | // just grab it again if it's still contained. |
| 3115 | if (currentGrabItems && currentGrabItems->contains(t: item)) { |
| 3116 | grabber->grab(item); |
| 3117 | grabber->setTarget(item); |
| 3118 | return true; |
| 3119 | } |
| 3120 | |
| 3121 | if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) { |
| 3122 | QDragMoveEvent translatedEvent( |
| 3123 | p.toPoint(), |
| 3124 | event->possibleActions(), |
| 3125 | event->mimeData(), |
| 3126 | event->mouseButtons(), |
| 3127 | event->keyboardModifiers(), |
| 3128 | event->type()); |
| 3129 | QQuickDropEventEx::copyActions(to: &translatedEvent, from: *event); |
| 3130 | translatedEvent.setAccepted(event->isAccepted()); |
| 3131 | QCoreApplication::sendEvent(receiver: item, event: &translatedEvent); |
| 3132 | event->setAccepted(translatedEvent.isAccepted()); |
| 3133 | event->setDropAction(translatedEvent.dropAction()); |
| 3134 | if (event->type() == QEvent::DragEnter) { |
| 3135 | if (translatedEvent.isAccepted()) { |
| 3136 | grabber->grab(item); |
| 3137 | grabber->setTarget(item); |
| 3138 | return true; |
| 3139 | } |
| 3140 | } else { |
| 3141 | return true; |
| 3142 | } |
| 3143 | } |
| 3144 | } |
| 3145 | |
| 3146 | // Check children behind this item if this item or any higher children have not accepted |
| 3147 | for (int ii = children.count() - 1; ii >= 0; --ii) { |
| 3148 | if (children.at(i: ii)->z() >= 0) |
| 3149 | continue; |
| 3150 | if (deliverDragEvent(grabber, item: children.at(i: ii), event: &enterEvent, currentGrabItems)) |
| 3151 | return true; |
| 3152 | } |
| 3153 | |
| 3154 | return false; |
| 3155 | } |
| 3156 | #endif // quick_draganddrop |
| 3157 | |
| 3158 | #if QT_CONFIG(cursor) |
| 3159 | void QQuickWindowPrivate::updateCursor(const QPointF &scenePos) |
| 3160 | { |
| 3161 | Q_Q(QQuickWindow); |
| 3162 | |
| 3163 | auto cursorItemAndHandler = findCursorItemAndHandler(item: contentItem, scenePos); |
| 3164 | |
| 3165 | if (cursorItem != cursorItemAndHandler.first || cursorHandler != cursorItemAndHandler.second) { |
| 3166 | QWindow *renderWindow = QQuickRenderControl::renderWindowFor(win: q); |
| 3167 | QWindow *window = renderWindow ? renderWindow : q; |
| 3168 | cursorItem = cursorItemAndHandler.first; |
| 3169 | cursorHandler = cursorItemAndHandler.second; |
| 3170 | if (cursorItem) |
| 3171 | window->setCursor(QQuickItemPrivate::get(item: cursorItem)->effectiveCursor(handler: cursorHandler)); |
| 3172 | else |
| 3173 | window->unsetCursor(); |
| 3174 | } |
| 3175 | } |
| 3176 | |
| 3177 | QPair<QQuickItem*, QQuickPointerHandler*> QQuickWindowPrivate::findCursorItemAndHandler(QQuickItem *item, const QPointF &scenePos) const |
| 3178 | { |
| 3179 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
| 3180 | if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { |
| 3181 | QPointF p = item->mapFromScene(point: scenePos); |
| 3182 | if (!item->contains(point: p)) |
| 3183 | return {nullptr, nullptr}; |
| 3184 | } |
| 3185 | |
| 3186 | if (itemPrivate->subtreeCursorEnabled) { |
| 3187 | QList<QQuickItem *> children = itemPrivate->paintOrderChildItems(); |
| 3188 | for (int ii = children.count() - 1; ii >= 0; --ii) { |
| 3189 | QQuickItem *child = children.at(i: ii); |
| 3190 | if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(item: child)->culled) |
| 3191 | continue; |
| 3192 | auto ret = findCursorItemAndHandler(item: child, scenePos); |
| 3193 | if (ret.first) |
| 3194 | return ret; |
| 3195 | } |
| 3196 | if (itemPrivate->hasCursorHandler) { |
| 3197 | if (auto handler = itemPrivate->effectiveCursorHandler()) { |
| 3198 | QQuickPointerEvent *pointerEvent = pointerEventInstance(device: QQuickPointerDevice::genericMouseDevice(), eventType: QEvent::MouseMove); |
| 3199 | pointerEvent->point(i: 0)->reset(state: Qt::TouchPointMoved, scenePosition: scenePos, pointId: quint64(1) << 24 /* mouse has device ID 1 */, timestamp: 0); |
| 3200 | pointerEvent->point(i: 0)->setAccepted(true); |
| 3201 | pointerEvent->localize(target: item); |
| 3202 | if (handler->parentContains(point: pointerEvent->point(i: 0))) |
| 3203 | return {item, handler}; |
| 3204 | } |
| 3205 | } |
| 3206 | if (itemPrivate->hasCursor) { |
| 3207 | QPointF p = item->mapFromScene(point: scenePos); |
| 3208 | if (item->contains(point: p)) |
| 3209 | return {item, nullptr}; |
| 3210 | } |
| 3211 | } |
| 3212 | |
| 3213 | return {nullptr, nullptr}; |
| 3214 | } |
| 3215 | #endif |
| 3216 | |
| 3217 | bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent) |
| 3218 | { |
| 3219 | return sendFilteredPointerEventImpl(event, receiver, filteringParent: filteringParent ? filteringParent : receiver->parentItem()); |
| 3220 | } |
| 3221 | |
| 3222 | bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent) |
| 3223 | { |
| 3224 | if (!allowChildEventFiltering) |
| 3225 | return false; |
| 3226 | if (!filteringParent) |
| 3227 | return false; |
| 3228 | bool filtered = false; |
| 3229 | if (filteringParent->filtersChildMouseEvents() && !hasFiltered.contains(t: filteringParent)) { |
| 3230 | hasFiltered.append(t: filteringParent); |
| 3231 | if (QQuickPointerMouseEvent *pme = event->asPointerMouseEvent()) { |
| 3232 | if (receiver->acceptedMouseButtons()) { |
| 3233 | QPointF localPos = receiver->mapFromScene(point: pme->point(i: 0)->scenePosition()); |
| 3234 | QMouseEvent *me = pme->asMouseEvent(localPos); |
| 3235 | const bool wasAccepted = me->isAccepted(); |
| 3236 | me->setAccepted(true); |
| 3237 | auto oldMouseGrabber = pme->point(i: 0)->grabberItem(); |
| 3238 | if (filteringParent->childMouseEventFilter(receiver, me)) { |
| 3239 | qCDebug(DBG_MOUSE) << "mouse event intercepted by childMouseEventFilter of " << filteringParent; |
| 3240 | skipDelivery.append(t: filteringParent); |
| 3241 | filtered = true; |
| 3242 | if (me->isAccepted() && pme->isPressEvent()) { |
| 3243 | auto mouseGrabber = pme->point(i: 0)->grabberItem(); |
| 3244 | if (mouseGrabber && mouseGrabber != receiver && mouseGrabber != oldMouseGrabber) { |
| 3245 | receiver->mouseUngrabEvent(); |
| 3246 | } else { |
| 3247 | pme->point(i: 0)->setGrabberItem(receiver); |
| 3248 | } |
| 3249 | } |
| 3250 | } else { |
| 3251 | // Restore accepted state if the event was not filtered. |
| 3252 | me->setAccepted(wasAccepted); |
| 3253 | } |
| 3254 | } |
| 3255 | } else if (QQuickPointerTouchEvent *pte = event->asPointerTouchEvent()) { |
| 3256 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
| 3257 | bool acceptsTouchEvents = receiver->acceptTouchEvents(); |
| 3258 | #else |
| 3259 | // In versions prior to Qt 6, we can't trust item->acceptTouchEvents() here, because it defaults to true. |
| 3260 | bool acceptsTouchEvents = false; |
| 3261 | #endif |
| 3262 | auto device = pte->device(); |
| 3263 | if (device->type() == QQuickPointerDevice::TouchPad && |
| 3264 | device->capabilities().testFlag(flag: QQuickPointerDevice::MouseEmulation)) { |
| 3265 | qCDebug(DBG_TOUCH_TARGET) << "skipping filtering of synth-mouse event from" << device; |
| 3266 | } else if (acceptsTouchEvents || receiver->acceptedMouseButtons()) { |
| 3267 | // get a touch event customized for delivery to filteringParent |
| 3268 | QScopedPointer<QTouchEvent> filteringParentTouchEvent(pte->touchEventForItem(item: receiver, isFiltering: true)); |
| 3269 | if (filteringParentTouchEvent) { |
| 3270 | if (filteringParent->childMouseEventFilter(receiver, filteringParentTouchEvent.data())) { |
| 3271 | qCDebug(DBG_TOUCH) << "touch event intercepted by childMouseEventFilter of " << filteringParent; |
| 3272 | skipDelivery.append(t: filteringParent); |
| 3273 | for (const auto &point: qAsConst(t: filteringParentTouchEvent->touchPoints())) { |
| 3274 | QQuickEventPoint *pt = event->pointById(pointId: point.id()); |
| 3275 | pt->setAccepted(); |
| 3276 | pt->setGrabberItem(filteringParent); |
| 3277 | } |
| 3278 | return true; |
| 3279 | } else if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) { |
| 3280 | // filteringParent didn't filter the touch event. Give it a chance to filter a synthetic mouse event. |
| 3281 | for (int i = 0; i < filteringParentTouchEvent->touchPoints().size(); ++i) { |
| 3282 | const QTouchEvent::TouchPoint &tp = filteringParentTouchEvent->touchPoints().at(i); |
| 3283 | |
| 3284 | QEvent::Type t; |
| 3285 | switch (tp.state()) { |
| 3286 | case Qt::TouchPointPressed: |
| 3287 | t = QEvent::MouseButtonPress; |
| 3288 | break; |
| 3289 | case Qt::TouchPointReleased: |
| 3290 | t = QEvent::MouseButtonRelease; |
| 3291 | break; |
| 3292 | case Qt::TouchPointStationary: |
| 3293 | continue; |
| 3294 | default: |
| 3295 | t = QEvent::MouseMove; |
| 3296 | break; |
| 3297 | } |
| 3298 | |
| 3299 | bool touchMouseUnset = (touchMouseId == -1); |
| 3300 | // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId |
| 3301 | if (touchMouseUnset || touchMouseId == tp.id()) { |
| 3302 | // convert filteringParentTouchEvent (which is already transformed wrt local position, velocity, etc.) |
| 3303 | // into a synthetic mouse event, and let childMouseEventFilter() have another chance with that |
| 3304 | QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(type: t, p: tp, event: filteringParentTouchEvent.data(), item: receiver, transformNeeded: false)); |
| 3305 | // If a filtering item calls QQuickWindow::mouseGrabberItem(), it should |
| 3306 | // report the touchpoint's grabber. Whenever we send a synthetic mouse event, |
| 3307 | // touchMouseId and touchMouseDevice must be set, even if it's only temporarily and isn't grabbed. |
| 3308 | touchMouseId = tp.id(); |
| 3309 | touchMouseDevice = event->device(); |
| 3310 | QQuickPointerDevice *dev = touchMouseDevice; |
| 3311 | if (filteringParent->childMouseEventFilter(receiver, mouseEvent.data())) { |
| 3312 | qCDebug(DBG_TOUCH) << "touch event intercepted as synth mouse event by childMouseEventFilter of " << filteringParent; |
| 3313 | skipDelivery.append(t: filteringParent); |
| 3314 | if (t != QEvent::MouseButtonRelease) { |
| 3315 | qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << Qt::hex << tp.id() << "->" << filteringParent; |
| 3316 | pointerEventInstance(device: dev)->pointById(pointId: tp.id())->setGrabberItem(filteringParent); |
| 3317 | touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set |
| 3318 | if (mouseEvent->isAccepted()) |
| 3319 | filteringParent->grabMouse(); |
| 3320 | } |
| 3321 | filtered = true; |
| 3322 | } |
| 3323 | if (touchMouseUnset) { |
| 3324 | // Now that we're done sending a synth mouse event, and it wasn't grabbed, |
| 3325 | // the touchpoint is no longer acting as a synthetic mouse. Restore previous state. |
| 3326 | cancelTouchMouseSynthesis(); |
| 3327 | } |
| 3328 | // Only one touchpoint can be treated as a synthetic mouse, so after childMouseEventFilter |
| 3329 | // has been called once, we're done with this loop over the touchpoints. |
| 3330 | break; |
| 3331 | } |
| 3332 | } |
| 3333 | } |
| 3334 | } |
| 3335 | } |
| 3336 | } |
| 3337 | } |
| 3338 | return sendFilteredPointerEventImpl(event, receiver, filteringParent: filteringParent->parentItem()) || filtered; |
| 3339 | } |
| 3340 | |
| 3341 | bool QQuickWindowPrivate::sendFilteredMouseEvent(QEvent *event, QQuickItem *receiver, QQuickItem *filteringParent) |
| 3342 | { |
| 3343 | if (!filteringParent) |
| 3344 | return false; |
| 3345 | |
| 3346 | QQuickItemPrivate *filteringParentPrivate = QQuickItemPrivate::get(item: filteringParent); |
| 3347 | if (filteringParentPrivate->replayingPressEvent) |
| 3348 | return false; |
| 3349 | |
| 3350 | bool filtered = false; |
| 3351 | if (filteringParentPrivate->filtersChildMouseEvents && !hasFiltered.contains(t: filteringParent)) { |
| 3352 | hasFiltered.append(t: filteringParent); |
| 3353 | if (filteringParent->childMouseEventFilter(receiver, event)) { |
| 3354 | filtered = true; |
| 3355 | skipDelivery.append(t: filteringParent); |
| 3356 | } |
| 3357 | qCDebug(DBG_MOUSE_TARGET) << "for" << receiver << filteringParent << "childMouseEventFilter ->" << filtered; |
| 3358 | } |
| 3359 | |
| 3360 | return sendFilteredMouseEvent(event, receiver, filteringParent: filteringParent->parentItem()) || filtered; |
| 3361 | } |
| 3362 | |
| 3363 | bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold) |
| 3364 | { |
| 3365 | QStyleHints *styleHints = QGuiApplication::styleHints(); |
| 3366 | int caps = QGuiApplicationPrivate::mouseEventCaps(event); |
| 3367 | bool dragVelocityLimitAvailable = (caps & QTouchDevice::Velocity) |
| 3368 | && styleHints->startDragVelocity(); |
| 3369 | bool overThreshold = qAbs(t: d) > (startDragThreshold >= 0 ? startDragThreshold : styleHints->startDragDistance()); |
| 3370 | if (dragVelocityLimitAvailable) { |
| 3371 | QVector2D velocityVec = QGuiApplicationPrivate::mouseEventVelocity(event); |
| 3372 | qreal velocity = axis == Qt::XAxis ? velocityVec.x() : velocityVec.y(); |
| 3373 | overThreshold |= qAbs(t: velocity) > styleHints->startDragVelocity(); |
| 3374 | } |
| 3375 | return overThreshold; |
| 3376 | } |
| 3377 | |
| 3378 | bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const QTouchEvent::TouchPoint *tp, int startDragThreshold) |
| 3379 | { |
| 3380 | QStyleHints *styleHints = qApp->styleHints(); |
| 3381 | bool overThreshold = qAbs(t: d) > (startDragThreshold >= 0 ? startDragThreshold : styleHints->startDragDistance()); |
| 3382 | const bool dragVelocityLimitAvailable = (styleHints->startDragVelocity() > 0); |
| 3383 | if (!overThreshold && dragVelocityLimitAvailable) { |
| 3384 | qreal velocity = axis == Qt::XAxis ? tp->velocity().x() : tp->velocity().y(); |
| 3385 | overThreshold |= qAbs(t: velocity) > styleHints->startDragVelocity(); |
| 3386 | } |
| 3387 | return overThreshold; |
| 3388 | } |
| 3389 | |
| 3390 | bool QQuickWindowPrivate::dragOverThreshold(QVector2D delta) |
| 3391 | { |
| 3392 | int threshold = qApp->styleHints()->startDragDistance(); |
| 3393 | return qAbs(t: delta.x()) > threshold || qAbs(t: delta.y()) > threshold; |
| 3394 | } |
| 3395 | |
| 3396 | /*! |
| 3397 | \qmlproperty list<Object> Window::data |
| 3398 | \default |
| 3399 | |
| 3400 | The data property allows you to freely mix visual children, resources |
| 3401 | and other Windows in a Window. |
| 3402 | |
| 3403 | If you assign another Window to the data list, the nested window will |
| 3404 | become "transient for" the outer Window. |
| 3405 | |
| 3406 | If you assign an \l Item to the data list, it becomes a child of the |
| 3407 | Window's \l contentItem, so that it appears inside the window. The item's |
| 3408 | parent will be the window's contentItem, which is the root of the Item |
| 3409 | ownership tree within that Window. |
| 3410 | |
| 3411 | If you assign any other object type, it is added as a resource. |
| 3412 | |
| 3413 | It should not generally be necessary to refer to the \c data property, |
| 3414 | as it is the default property for Window and thus all child items are |
| 3415 | automatically assigned to this property. |
| 3416 | |
| 3417 | \sa QWindow::transientParent() |
| 3418 | */ |
| 3419 | |
| 3420 | void QQuickWindowPrivate::data_append(QQmlListProperty<QObject> *property, QObject *o) |
| 3421 | { |
| 3422 | if (!o) |
| 3423 | return; |
| 3424 | QQuickWindow *that = static_cast<QQuickWindow *>(property->object); |
| 3425 | if (QQuickWindow *window = qmlobject_cast<QQuickWindow *>(object: o)) { |
| 3426 | qCDebug(lcTransient) << window << "is transient for" << that; |
| 3427 | window->setTransientParent(that); |
| 3428 | } |
| 3429 | QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: that->contentItem())->data(); |
| 3430 | itemProperty.append(&itemProperty, o); |
| 3431 | } |
| 3432 | |
| 3433 | int QQuickWindowPrivate::data_count(QQmlListProperty<QObject> *property) |
| 3434 | { |
| 3435 | QQuickWindow *win = static_cast<QQuickWindow*>(property->object); |
| 3436 | if (!win || !win->contentItem() || !QQuickItemPrivate::get(item: win->contentItem())->data().count) |
| 3437 | return 0; |
| 3438 | QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: win->contentItem())->data(); |
| 3439 | return itemProperty.count(&itemProperty); |
| 3440 | } |
| 3441 | |
| 3442 | QObject *QQuickWindowPrivate::data_at(QQmlListProperty<QObject> *property, int i) |
| 3443 | { |
| 3444 | QQuickWindow *win = static_cast<QQuickWindow*>(property->object); |
| 3445 | QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: win->contentItem())->data(); |
| 3446 | return itemProperty.at(&itemProperty, i); |
| 3447 | } |
| 3448 | |
| 3449 | void QQuickWindowPrivate::data_clear(QQmlListProperty<QObject> *property) |
| 3450 | { |
| 3451 | QQuickWindow *win = static_cast<QQuickWindow*>(property->object); |
| 3452 | QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: win->contentItem())->data(); |
| 3453 | itemProperty.clear(&itemProperty); |
| 3454 | } |
| 3455 | |
| 3456 | void QQuickWindowPrivate::data_replace(QQmlListProperty<QObject> *property, int i, QObject *o) |
| 3457 | { |
| 3458 | QQuickWindow *win = static_cast<QQuickWindow*>(property->object); |
| 3459 | QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: win->contentItem())->data(); |
| 3460 | itemProperty.replace(&itemProperty, i, o); |
| 3461 | } |
| 3462 | |
| 3463 | void QQuickWindowPrivate::data_removeLast(QQmlListProperty<QObject> *property) |
| 3464 | { |
| 3465 | QQuickWindow *win = static_cast<QQuickWindow*>(property->object); |
| 3466 | QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: win->contentItem())->data(); |
| 3467 | itemProperty.removeLast(&itemProperty); |
| 3468 | } |
| 3469 | |
| 3470 | bool QQuickWindowPrivate::isRenderable() const |
| 3471 | { |
| 3472 | Q_Q(const QQuickWindow); |
| 3473 | return ((q->isExposed() && q->isVisible())) && q->geometry().isValid(); |
| 3474 | } |
| 3475 | |
| 3476 | void QQuickWindowPrivate::contextCreationFailureMessage(const QSurfaceFormat &format, |
| 3477 | QString *translatedMessage, |
| 3478 | QString *untranslatedMessage) |
| 3479 | { |
| 3480 | const QString contextType = QLatin1String("OpenGL" ); |
| 3481 | QString formatStr; |
| 3482 | QDebug(&formatStr) << format; |
| 3483 | #if defined(Q_OS_WIN32) |
| 3484 | const bool isDebug = QLibraryInfo::isDebugBuild(); |
| 3485 | const QString eglLibName = QLatin1String(isDebug ? "libEGLd.dll" : "libEGL.dll" ); |
| 3486 | const QString glesLibName = QLatin1String(isDebug ? "libGLESv2d.dll" : "libGLESv2.dll" ); |
| 3487 | //: %1 Context type (Open GL, EGL), %2 format, ANGLE %3, %4 library names |
| 3488 | const char msg[] = QT_TRANSLATE_NOOP("QQuickWindow" , |
| 3489 | "Failed to create %1 context for format %2.\n" |
| 3490 | "This is most likely caused by not having the necessary graphics drivers installed.\n\n" |
| 3491 | "Install a driver providing OpenGL 2.0 or higher, or, if this is not possible, " |
| 3492 | "make sure the ANGLE Open GL ES 2.0 emulation libraries (%3, %4 and d3dcompiler_*.dll) " |
| 3493 | "are available in the application executable's directory or in a location listed in PATH." ); |
| 3494 | *translatedMessage = QQuickWindow::tr(msg).arg(contextType, formatStr, eglLibName, glesLibName); |
| 3495 | *untranslatedMessage = QString::fromLatin1(msg).arg(contextType, formatStr, eglLibName, glesLibName); |
| 3496 | #else // Q_OS_WIN32 |
| 3497 | //: %1 Context type (Open GL, EGL), %2 format specification |
| 3498 | const char msg[] = QT_TRANSLATE_NOOP("QQuickWindow" , |
| 3499 | "Failed to create %1 context for format %2" ); |
| 3500 | *translatedMessage = QQuickWindow::tr(s: msg).arg(args: contextType, args&: formatStr); |
| 3501 | *untranslatedMessage = QString::fromLatin1(str: msg).arg(args: contextType, args&: formatStr); |
| 3502 | #endif // !Q_OS_WIN32 |
| 3503 | } |
| 3504 | |
| 3505 | void QQuickWindowPrivate::rhiCreationFailureMessage(const QString &backendName, |
| 3506 | QString *translatedMessage, |
| 3507 | QString *untranslatedMessage) |
| 3508 | { |
| 3509 | const char msg[] = QT_TRANSLATE_NOOP("QQuickWindow" , |
| 3510 | "Failed to initialize graphics backend for %1." ); |
| 3511 | *translatedMessage = QQuickWindow::tr(s: msg).arg(a: backendName); |
| 3512 | *untranslatedMessage = QString::fromLatin1(str: msg).arg(a: backendName); |
| 3513 | } |
| 3514 | |
| 3515 | #if QT_DEPRECATED_SINCE(5, 8) |
| 3516 | |
| 3517 | // ### Qt6: remove |
| 3518 | /*! |
| 3519 | Propagates an event \a e to a QQuickItem \a item on the window. |
| 3520 | |
| 3521 | Use \l QCoreApplication::sendEvent() directly instead. |
| 3522 | |
| 3523 | The return value is currently not used. |
| 3524 | |
| 3525 | \deprecated |
| 3526 | */ |
| 3527 | bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e) |
| 3528 | { |
| 3529 | Q_D(QQuickWindow); |
| 3530 | |
| 3531 | if (!item) { |
| 3532 | qWarning(msg: "QQuickWindow::sendEvent: Cannot send event to a null item" ); |
| 3533 | return false; |
| 3534 | } |
| 3535 | |
| 3536 | Q_ASSERT(e); |
| 3537 | |
| 3538 | switch (e->type()) { |
| 3539 | case QEvent::KeyPress: |
| 3540 | case QEvent::KeyRelease: |
| 3541 | e->accept(); |
| 3542 | QCoreApplication::sendEvent(receiver: item, event: e); |
| 3543 | while (!e->isAccepted() && (item = item->parentItem())) { |
| 3544 | e->accept(); |
| 3545 | QCoreApplication::sendEvent(receiver: item, event: e); |
| 3546 | } |
| 3547 | break; |
| 3548 | case QEvent::MouseButtonPress: |
| 3549 | case QEvent::MouseButtonRelease: |
| 3550 | case QEvent::MouseButtonDblClick: |
| 3551 | case QEvent::MouseMove: { |
| 3552 | // XXX todo - should sendEvent be doing this? how does it relate to forwarded events? |
| 3553 | d->hasFiltered.clear(); |
| 3554 | if (!d->sendFilteredMouseEvent(event: e, receiver: item, filteringParent: item->parentItem())) { |
| 3555 | // accept because qml items by default accept and have to explicitly opt out of accepting |
| 3556 | e->accept(); |
| 3557 | QCoreApplication::sendEvent(receiver: item, event: e); |
| 3558 | } |
| 3559 | } |
| 3560 | break; |
| 3561 | default: |
| 3562 | QCoreApplication::sendEvent(receiver: item, event: e); |
| 3563 | break; |
| 3564 | } |
| 3565 | |
| 3566 | return false; |
| 3567 | } |
| 3568 | |
| 3569 | #endif |
| 3570 | |
| 3571 | void QQuickWindowPrivate::cleanupNodes() |
| 3572 | { |
| 3573 | for (int ii = 0; ii < cleanupNodeList.count(); ++ii) |
| 3574 | delete cleanupNodeList.at(i: ii); |
| 3575 | cleanupNodeList.clear(); |
| 3576 | } |
| 3577 | |
| 3578 | void QQuickWindowPrivate::cleanupNodesOnShutdown(QQuickItem *item) |
| 3579 | { |
| 3580 | QQuickItemPrivate *p = QQuickItemPrivate::get(item); |
| 3581 | if (p->itemNodeInstance) { |
| 3582 | delete p->itemNodeInstance; |
| 3583 | p->itemNodeInstance = nullptr; |
| 3584 | |
| 3585 | if (p->extra.isAllocated()) { |
| 3586 | p->extra->opacityNode = nullptr; |
| 3587 | p->extra->clipNode = nullptr; |
| 3588 | p->extra->rootNode = nullptr; |
| 3589 | } |
| 3590 | |
| 3591 | p->paintNode = nullptr; |
| 3592 | |
| 3593 | p->dirty(QQuickItemPrivate::Window); |
| 3594 | } |
| 3595 | |
| 3596 | // Qt 6: Make invalidateSceneGraph a virtual member of QQuickItem |
| 3597 | if (p->flags & QQuickItem::ItemHasContents) { |
| 3598 | const QMetaObject *mo = item->metaObject(); |
| 3599 | int index = mo->indexOfSlot(slot: "invalidateSceneGraph()" ); |
| 3600 | if (index >= 0) { |
| 3601 | const QMetaMethod &method = mo->method(index); |
| 3602 | // Skip functions named invalidateSceneGraph() in QML items. |
| 3603 | if (strstr(haystack: method.enclosingMetaObject()->className(), needle: "_QML_" ) == nullptr) |
| 3604 | method.invoke(object: item, connectionType: Qt::DirectConnection); |
| 3605 | } |
| 3606 | } |
| 3607 | |
| 3608 | for (int ii = 0; ii < p->childItems.count(); ++ii) |
| 3609 | cleanupNodesOnShutdown(item: p->childItems.at(i: ii)); |
| 3610 | } |
| 3611 | |
| 3612 | // This must be called from the render thread, with the main thread frozen |
| 3613 | void QQuickWindowPrivate::cleanupNodesOnShutdown() |
| 3614 | { |
| 3615 | Q_Q(QQuickWindow); |
| 3616 | cleanupNodes(); |
| 3617 | cleanupNodesOnShutdown(item: contentItem); |
| 3618 | for (QSet<QQuickItem *>::const_iterator it = parentlessItems.begin(), cend = parentlessItems.end(); it != cend; ++it) |
| 3619 | cleanupNodesOnShutdown(item: *it); |
| 3620 | animationController->windowNodesDestroyed(); |
| 3621 | q->cleanupSceneGraph(); |
| 3622 | } |
| 3623 | |
| 3624 | void QQuickWindowPrivate::updateDirtyNodes() |
| 3625 | { |
| 3626 | qCDebug(DBG_DIRTY) << "QQuickWindowPrivate::updateDirtyNodes():" ; |
| 3627 | |
| 3628 | cleanupNodes(); |
| 3629 | |
| 3630 | QQuickItem *updateList = dirtyItemList; |
| 3631 | dirtyItemList = nullptr; |
| 3632 | if (updateList) QQuickItemPrivate::get(item: updateList)->prevDirtyItem = &updateList; |
| 3633 | |
| 3634 | while (updateList) { |
| 3635 | QQuickItem *item = updateList; |
| 3636 | QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); |
| 3637 | itemPriv->removeFromDirtyList(); |
| 3638 | |
| 3639 | qCDebug(DBG_DIRTY) << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString()); |
| 3640 | updateDirtyNode(item); |
| 3641 | } |
| 3642 | } |
| 3643 | |
| 3644 | static inline QSGNode *qquickitem_before_paintNode(QQuickItemPrivate *d) |
| 3645 | { |
| 3646 | const QList<QQuickItem *> childItems = d->paintOrderChildItems(); |
| 3647 | QQuickItem *before = nullptr; |
| 3648 | for (int i=0; i<childItems.size(); ++i) { |
| 3649 | QQuickItemPrivate *dd = QQuickItemPrivate::get(item: childItems.at(i)); |
| 3650 | // Perform the same check as the in fetchNextNode below. |
| 3651 | if (dd->z() < 0 && (dd->explicitVisible || (dd->extra.isAllocated() && dd->extra->effectRefCount))) |
| 3652 | before = childItems.at(i); |
| 3653 | else |
| 3654 | break; |
| 3655 | } |
| 3656 | return Q_UNLIKELY(before) ? QQuickItemPrivate::get(item: before)->itemNode() : nullptr; |
| 3657 | } |
| 3658 | |
| 3659 | static QSGNode *fetchNextNode(QQuickItemPrivate *itemPriv, int &ii, bool &returnedPaintNode) |
| 3660 | { |
| 3661 | QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems(); |
| 3662 | |
| 3663 | for (; ii < orderedChildren.count() && orderedChildren.at(i: ii)->z() < 0; ++ii) { |
| 3664 | QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(item: orderedChildren.at(i: ii)); |
| 3665 | if (!childPrivate->explicitVisible && |
| 3666 | (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount)) |
| 3667 | continue; |
| 3668 | |
| 3669 | ii++; |
| 3670 | return childPrivate->itemNode(); |
| 3671 | } |
| 3672 | |
| 3673 | if (itemPriv->paintNode && !returnedPaintNode) { |
| 3674 | returnedPaintNode = true; |
| 3675 | return itemPriv->paintNode; |
| 3676 | } |
| 3677 | |
| 3678 | for (; ii < orderedChildren.count(); ++ii) { |
| 3679 | QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(item: orderedChildren.at(i: ii)); |
| 3680 | if (!childPrivate->explicitVisible && |
| 3681 | (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount)) |
| 3682 | continue; |
| 3683 | |
| 3684 | ii++; |
| 3685 | return childPrivate->itemNode(); |
| 3686 | } |
| 3687 | |
| 3688 | return nullptr; |
| 3689 | } |
| 3690 | |
| 3691 | void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item) |
| 3692 | { |
| 3693 | QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); |
| 3694 | quint32 dirty = itemPriv->dirtyAttributes; |
| 3695 | itemPriv->dirtyAttributes = 0; |
| 3696 | |
| 3697 | if ((dirty & QQuickItemPrivate::TransformUpdateMask) || |
| 3698 | (dirty & QQuickItemPrivate::Size && itemPriv->origin() != QQuickItem::TopLeft && |
| 3699 | (itemPriv->scale() != 1. || itemPriv->rotation() != 0.))) { |
| 3700 | |
| 3701 | QMatrix4x4 matrix; |
| 3702 | |
| 3703 | if (itemPriv->x != 0. || itemPriv->y != 0.) |
| 3704 | matrix.translate(x: itemPriv->x, y: itemPriv->y); |
| 3705 | |
| 3706 | for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii) |
| 3707 | itemPriv->transforms.at(i: ii)->applyTo(matrix: &matrix); |
| 3708 | |
| 3709 | if (itemPriv->scale() != 1. || itemPriv->rotation() != 0.) { |
| 3710 | QPointF origin = item->transformOriginPoint(); |
| 3711 | matrix.translate(x: origin.x(), y: origin.y()); |
| 3712 | if (itemPriv->scale() != 1.) |
| 3713 | matrix.scale(x: itemPriv->scale(), y: itemPriv->scale()); |
| 3714 | if (itemPriv->rotation() != 0.) |
| 3715 | matrix.rotate(angle: itemPriv->rotation(), x: 0, y: 0, z: 1); |
| 3716 | matrix.translate(x: -origin.x(), y: -origin.y()); |
| 3717 | } |
| 3718 | |
| 3719 | itemPriv->itemNode()->setMatrix(matrix); |
| 3720 | } |
| 3721 | |
| 3722 | bool clipEffectivelyChanged = (dirty & (QQuickItemPrivate::Clip | QQuickItemPrivate::Window)) && |
| 3723 | ((item->clip() == false) != (itemPriv->clipNode() == nullptr)); |
| 3724 | int effectRefCount = itemPriv->extra.isAllocated()?itemPriv->extra->effectRefCount:0; |
| 3725 | bool effectRefEffectivelyChanged = (dirty & (QQuickItemPrivate::EffectReference | QQuickItemPrivate::Window)) && |
| 3726 | ((effectRefCount == 0) != (itemPriv->rootNode() == nullptr)); |
| 3727 | |
| 3728 | if (clipEffectivelyChanged) { |
| 3729 | QSGNode *parent = itemPriv->opacityNode() ? (QSGNode *) itemPriv->opacityNode() : |
| 3730 | (QSGNode *) itemPriv->itemNode(); |
| 3731 | QSGNode *child = itemPriv->rootNode(); |
| 3732 | |
| 3733 | if (item->clip()) { |
| 3734 | Q_ASSERT(itemPriv->clipNode() == nullptr); |
| 3735 | QQuickDefaultClipNode *clip = new QQuickDefaultClipNode(item->clipRect()); |
| 3736 | itemPriv->extra.value().clipNode = clip; |
| 3737 | clip->update(); |
| 3738 | |
| 3739 | if (!child) { |
| 3740 | parent->reparentChildNodesTo(newParent: clip); |
| 3741 | parent->appendChildNode(node: clip); |
| 3742 | } else { |
| 3743 | parent->removeChildNode(node: child); |
| 3744 | clip->appendChildNode(node: child); |
| 3745 | parent->appendChildNode(node: clip); |
| 3746 | } |
| 3747 | |
| 3748 | } else { |
| 3749 | QQuickDefaultClipNode *clip = itemPriv->clipNode(); |
| 3750 | Q_ASSERT(clip); |
| 3751 | parent->removeChildNode(node: clip); |
| 3752 | if (child) { |
| 3753 | clip->removeChildNode(node: child); |
| 3754 | parent->appendChildNode(node: child); |
| 3755 | } else { |
| 3756 | clip->reparentChildNodesTo(newParent: parent); |
| 3757 | } |
| 3758 | |
| 3759 | delete itemPriv->clipNode(); |
| 3760 | itemPriv->extra->clipNode = nullptr; |
| 3761 | } |
| 3762 | } |
| 3763 | |
| 3764 | if (effectRefEffectivelyChanged) { |
| 3765 | if (dirty & QQuickItemPrivate::ChildrenUpdateMask) |
| 3766 | itemPriv->childContainerNode()->removeAllChildNodes(); |
| 3767 | |
| 3768 | QSGNode *parent = itemPriv->clipNode(); |
| 3769 | if (!parent) |
| 3770 | parent = itemPriv->opacityNode(); |
| 3771 | if (!parent) |
| 3772 | parent = itemPriv->itemNode(); |
| 3773 | |
| 3774 | if (itemPriv->extra.isAllocated() && itemPriv->extra->effectRefCount) { |
| 3775 | Q_ASSERT(itemPriv->rootNode() == nullptr); |
| 3776 | QSGRootNode *root = new QSGRootNode(); |
| 3777 | itemPriv->extra->rootNode = root; |
| 3778 | parent->reparentChildNodesTo(newParent: root); |
| 3779 | parent->appendChildNode(node: root); |
| 3780 | } else { |
| 3781 | Q_ASSERT(itemPriv->rootNode() != nullptr); |
| 3782 | QSGRootNode *root = itemPriv->rootNode(); |
| 3783 | parent->removeChildNode(node: root); |
| 3784 | root->reparentChildNodesTo(newParent: parent); |
| 3785 | delete itemPriv->rootNode(); |
| 3786 | itemPriv->extra->rootNode = nullptr; |
| 3787 | } |
| 3788 | } |
| 3789 | |
| 3790 | if (dirty & QQuickItemPrivate::ChildrenUpdateMask) { |
| 3791 | int ii = 0; |
| 3792 | bool fetchedPaintNode = false; |
| 3793 | QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems(); |
| 3794 | int desiredNodesSize = orderedChildren.size() + (itemPriv->paintNode ? 1 : 0); |
| 3795 | |
| 3796 | // now start making current state match the promised land of |
| 3797 | // desiredNodes. in the case of our current state matching desiredNodes |
| 3798 | // (though why would we get ChildrenUpdateMask with no changes?) then we |
| 3799 | // should make no changes at all. |
| 3800 | |
| 3801 | // how many nodes did we process, when examining changes |
| 3802 | int desiredNodesProcessed = 0; |
| 3803 | |
| 3804 | // currentNode is how far, in our present tree, we have processed. we |
| 3805 | // make use of this later on to trim the current child list if the |
| 3806 | // desired list is shorter. |
| 3807 | QSGNode *groupNode = itemPriv->childContainerNode(); |
| 3808 | QSGNode *currentNode = groupNode->firstChild(); |
| 3809 | int added = 0; |
| 3810 | int removed = 0; |
| 3811 | int replaced = 0; |
| 3812 | QSGNode *desiredNode = nullptr; |
| 3813 | |
| 3814 | while (currentNode && (desiredNode = fetchNextNode(itemPriv, ii, returnedPaintNode&: fetchedPaintNode))) { |
| 3815 | // uh oh... reality and our utopic paradise are diverging! |
| 3816 | // we need to reconcile this... |
| 3817 | if (currentNode != desiredNode) { |
| 3818 | // for now, we're just removing the node from the children - |
| 3819 | // and replacing it with the new node. |
| 3820 | if (desiredNode->parent()) |
| 3821 | desiredNode->parent()->removeChildNode(node: desiredNode); |
| 3822 | groupNode->insertChildNodeAfter(node: desiredNode, after: currentNode); |
| 3823 | groupNode->removeChildNode(node: currentNode); |
| 3824 | replaced++; |
| 3825 | |
| 3826 | // since we just replaced currentNode, we also need to reset |
| 3827 | // the pointer. |
| 3828 | currentNode = desiredNode; |
| 3829 | } |
| 3830 | |
| 3831 | currentNode = currentNode->nextSibling(); |
| 3832 | desiredNodesProcessed++; |
| 3833 | } |
| 3834 | |
| 3835 | // if we didn't process as many nodes as in the new list, then we have |
| 3836 | // more nodes at the end of desiredNodes to append to our list. |
| 3837 | // this will be the case when adding new nodes, for instance. |
| 3838 | if (desiredNodesProcessed < desiredNodesSize) { |
| 3839 | while ((desiredNode = fetchNextNode(itemPriv, ii, returnedPaintNode&: fetchedPaintNode))) { |
| 3840 | if (desiredNode->parent()) |
| 3841 | desiredNode->parent()->removeChildNode(node: desiredNode); |
| 3842 | groupNode->appendChildNode(node: desiredNode); |
| 3843 | added++; |
| 3844 | } |
| 3845 | } else if (currentNode) { |
| 3846 | // on the other hand, if we processed less than our current node |
| 3847 | // tree, then nodes have been _removed_ from the scene, and we need |
| 3848 | // to take care of that here. |
| 3849 | while (currentNode) { |
| 3850 | QSGNode *node = currentNode->nextSibling(); |
| 3851 | groupNode->removeChildNode(node: currentNode); |
| 3852 | currentNode = node; |
| 3853 | removed++; |
| 3854 | } |
| 3855 | } |
| 3856 | } |
| 3857 | |
| 3858 | if ((dirty & QQuickItemPrivate::Size) && itemPriv->clipNode()) { |
| 3859 | itemPriv->clipNode()->setRect(item->clipRect()); |
| 3860 | itemPriv->clipNode()->update(); |
| 3861 | } |
| 3862 | |
| 3863 | if (dirty & (QQuickItemPrivate::OpacityValue | QQuickItemPrivate::Visible |
| 3864 | | QQuickItemPrivate::HideReference | QQuickItemPrivate::Window)) |
| 3865 | { |
| 3866 | qreal opacity = itemPriv->explicitVisible && (!itemPriv->extra.isAllocated() || itemPriv->extra->hideRefCount == 0) |
| 3867 | ? itemPriv->opacity() : qreal(0); |
| 3868 | |
| 3869 | if (opacity != 1 && !itemPriv->opacityNode()) { |
| 3870 | QSGOpacityNode *node = new QSGOpacityNode; |
| 3871 | itemPriv->extra.value().opacityNode = node; |
| 3872 | |
| 3873 | QSGNode *parent = itemPriv->itemNode(); |
| 3874 | QSGNode *child = itemPriv->clipNode(); |
| 3875 | if (!child) |
| 3876 | child = itemPriv->rootNode(); |
| 3877 | |
| 3878 | if (child) { |
| 3879 | parent->removeChildNode(node: child); |
| 3880 | node->appendChildNode(node: child); |
| 3881 | parent->appendChildNode(node); |
| 3882 | } else { |
| 3883 | parent->reparentChildNodesTo(newParent: node); |
| 3884 | parent->appendChildNode(node); |
| 3885 | } |
| 3886 | } |
| 3887 | if (itemPriv->opacityNode()) |
| 3888 | itemPriv->opacityNode()->setOpacity(opacity); |
| 3889 | } |
| 3890 | |
| 3891 | if (dirty & QQuickItemPrivate::ContentUpdateMask) { |
| 3892 | |
| 3893 | if (itemPriv->flags & QQuickItem::ItemHasContents) { |
| 3894 | updatePaintNodeData.transformNode = itemPriv->itemNode(); |
| 3895 | itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData); |
| 3896 | |
| 3897 | Q_ASSERT(itemPriv->paintNode == nullptr || |
| 3898 | itemPriv->paintNode->parent() == nullptr || |
| 3899 | itemPriv->paintNode->parent() == itemPriv->childContainerNode()); |
| 3900 | |
| 3901 | if (itemPriv->paintNode && itemPriv->paintNode->parent() == nullptr) { |
| 3902 | QSGNode *before = qquickitem_before_paintNode(d: itemPriv); |
| 3903 | if (before && before->parent()) { |
| 3904 | Q_ASSERT(before->parent() == itemPriv->childContainerNode()); |
| 3905 | itemPriv->childContainerNode()->insertChildNodeAfter(node: itemPriv->paintNode, after: before); |
| 3906 | } else { |
| 3907 | itemPriv->childContainerNode()->prependChildNode(node: itemPriv->paintNode); |
| 3908 | } |
| 3909 | } |
| 3910 | } else if (itemPriv->paintNode) { |
| 3911 | delete itemPriv->paintNode; |
| 3912 | itemPriv->paintNode = nullptr; |
| 3913 | } |
| 3914 | } |
| 3915 | |
| 3916 | #ifndef QT_NO_DEBUG |
| 3917 | // Check consistency. |
| 3918 | |
| 3919 | QList<QSGNode *> nodes; |
| 3920 | nodes << itemPriv->itemNodeInstance |
| 3921 | << itemPriv->opacityNode() |
| 3922 | << itemPriv->clipNode() |
| 3923 | << itemPriv->rootNode() |
| 3924 | << itemPriv->paintNode; |
| 3925 | nodes.removeAll(t: 0); |
| 3926 | |
| 3927 | Q_ASSERT(nodes.constFirst() == itemPriv->itemNodeInstance); |
| 3928 | for (int i=1; i<nodes.size(); ++i) { |
| 3929 | QSGNode *n = nodes.at(i); |
| 3930 | // Failing this means we messed up reparenting |
| 3931 | Q_ASSERT(n->parent() == nodes.at(i-1)); |
| 3932 | // Only the paintNode and the one who is childContainer may have more than one child. |
| 3933 | Q_ASSERT(n == itemPriv->paintNode || n == itemPriv->childContainerNode() || n->childCount() == 1); |
| 3934 | } |
| 3935 | #endif |
| 3936 | |
| 3937 | } |
| 3938 | |
| 3939 | bool QQuickWindowPrivate::emitError(QQuickWindow::SceneGraphError error, const QString &msg) |
| 3940 | { |
| 3941 | Q_Q(QQuickWindow); |
| 3942 | static const QMetaMethod errorSignal = QMetaMethod::fromSignal(signal: &QQuickWindow::sceneGraphError); |
| 3943 | if (q->isSignalConnected(signal: errorSignal)) { |
| 3944 | emit q->sceneGraphError(error, message: msg); |
| 3945 | return true; |
| 3946 | } |
| 3947 | return false; |
| 3948 | } |
| 3949 | |
| 3950 | void QQuickWindow::maybeUpdate() |
| 3951 | { |
| 3952 | Q_D(QQuickWindow); |
| 3953 | if (d->renderControl) |
| 3954 | QQuickRenderControlPrivate::get(renderControl: d->renderControl)->maybeUpdate(); |
| 3955 | else if (d->windowManager) |
| 3956 | d->windowManager->maybeUpdate(window: this); |
| 3957 | } |
| 3958 | |
| 3959 | void QQuickWindow::cleanupSceneGraph() |
| 3960 | { |
| 3961 | Q_D(QQuickWindow); |
| 3962 | #if QT_CONFIG(opengl) |
| 3963 | delete d->vaoHelper; |
| 3964 | d->vaoHelper = nullptr; |
| 3965 | #endif |
| 3966 | if (!d->renderer) |
| 3967 | return; |
| 3968 | |
| 3969 | delete d->renderer->rootNode(); |
| 3970 | delete d->renderer; |
| 3971 | d->renderer = nullptr; |
| 3972 | |
| 3973 | d->runAndClearJobs(jobs: &d->beforeSynchronizingJobs); |
| 3974 | d->runAndClearJobs(jobs: &d->afterSynchronizingJobs); |
| 3975 | d->runAndClearJobs(jobs: &d->beforeRenderingJobs); |
| 3976 | d->runAndClearJobs(jobs: &d->afterRenderingJobs); |
| 3977 | d->runAndClearJobs(jobs: &d->afterSwapJobs); |
| 3978 | } |
| 3979 | |
| 3980 | void QQuickWindow::setTransientParent_helper(QQuickWindow *window) |
| 3981 | { |
| 3982 | qCDebug(lcTransient) << this << "is transient for" << window; |
| 3983 | setTransientParent(window); |
| 3984 | disconnect(sender: sender(), SIGNAL(windowChanged(QQuickWindow*)), |
| 3985 | receiver: this, SLOT(setTransientParent_helper(QQuickWindow*))); |
| 3986 | } |
| 3987 | |
| 3988 | /*! |
| 3989 | Returns the OpenGL context used for rendering. |
| 3990 | |
| 3991 | \note If the scene graph is not ready, or the scene graph is not using |
| 3992 | OpenGL (or RHI over OpenGL), this function will return null. |
| 3993 | |
| 3994 | \sa sceneGraphInitialized(), sceneGraphInvalidated() |
| 3995 | */ |
| 3996 | QOpenGLContext *QQuickWindow::openglContext() const |
| 3997 | { |
| 3998 | #if QT_CONFIG(opengl) |
| 3999 | Q_D(const QQuickWindow); |
| 4000 | if (d->context && d->context->isValid()) { |
| 4001 | QSGRendererInterface *rif = d->context->sceneGraphContext()->rendererInterface(renderContext: d->context); |
| 4002 | if (rif) { |
| 4003 | return reinterpret_cast<QOpenGLContext *>(rif->getResource(window: const_cast<QQuickWindow *>(this), |
| 4004 | resource: QSGRendererInterface::OpenGLContextResource)); |
| 4005 | } |
| 4006 | } |
| 4007 | #endif |
| 4008 | return nullptr; |
| 4009 | } |
| 4010 | |
| 4011 | /*! |
| 4012 | Returns true if the scene graph has been initialized; otherwise returns false. |
| 4013 | */ |
| 4014 | bool QQuickWindow::isSceneGraphInitialized() const |
| 4015 | { |
| 4016 | Q_D(const QQuickWindow); |
| 4017 | return d->context != nullptr && d->context->isValid(); |
| 4018 | } |
| 4019 | |
| 4020 | /*! |
| 4021 | \fn void QQuickWindow::frameSwapped() |
| 4022 | |
| 4023 | This signal is emitted when a frame has been queued for presenting. With |
| 4024 | vertical synchronization enabled the signal is emitted at most once per |
| 4025 | vsync interval in a continuously animating scene. |
| 4026 | |
| 4027 | This signal will be emitted from the scene graph rendering thread. |
| 4028 | */ |
| 4029 | |
| 4030 | /*! |
| 4031 | \qmlsignal QtQuick.Window::Window::frameSwapped() |
| 4032 | |
| 4033 | This signal is emitted when a frame has been queued for presenting. With |
| 4034 | vertical synchronization enabled the signal is emitted at most once per |
| 4035 | vsync interval in a continuously animating scene. |
| 4036 | */ |
| 4037 | |
| 4038 | /*! |
| 4039 | \fn void QQuickWindow::sceneGraphInitialized() |
| 4040 | |
| 4041 | This signal is emitted when the scene graph has been initialized. |
| 4042 | |
| 4043 | This signal will be emitted from the scene graph rendering thread. |
| 4044 | */ |
| 4045 | |
| 4046 | /*! |
| 4047 | \qmlsignal QtQuick.Window::Window::sceneGraphInitialized() |
| 4048 | \internal |
| 4049 | */ |
| 4050 | |
| 4051 | /*! |
| 4052 | \fn void QQuickWindow::sceneGraphInvalidated() |
| 4053 | |
| 4054 | This signal is emitted when the scene graph has been invalidated. |
| 4055 | |
| 4056 | This signal implies that the graphics rendering context used |
| 4057 | has been invalidated and all user resources tied to that context |
| 4058 | should be released. |
| 4059 | |
| 4060 | In the case of the default OpenGL adaptation the context of this |
| 4061 | window will be bound when this function is called. The only exception |
| 4062 | is if the native OpenGL has been destroyed outside Qt's control, |
| 4063 | for instance through EGL_CONTEXT_LOST. |
| 4064 | |
| 4065 | This signal will be emitted from the scene graph rendering thread. |
| 4066 | */ |
| 4067 | |
| 4068 | /*! |
| 4069 | \qmlsignal QtQuick.Window::Window::sceneGraphInvalidated() |
| 4070 | \internal |
| 4071 | */ |
| 4072 | |
| 4073 | /*! |
| 4074 | \fn void QQuickWindow::sceneGraphError(SceneGraphError error, const QString &message) |
| 4075 | |
| 4076 | This signal is emitted when an \a error occurred during scene graph initialization. |
| 4077 | |
| 4078 | Applications should connect to this signal if they wish to handle errors, |
| 4079 | like graphics context creation failures, in a custom way. When no slot is |
| 4080 | connected to the signal, the behavior will be different: Quick will print |
| 4081 | the \a message, or show a message box, and terminate the application. |
| 4082 | |
| 4083 | This signal will be emitted from the GUI thread. |
| 4084 | |
| 4085 | \since 5.3 |
| 4086 | */ |
| 4087 | |
| 4088 | /*! |
| 4089 | \qmlsignal QtQuick.Window::Window::sceneGraphError(SceneGraphError error, QString message) |
| 4090 | |
| 4091 | This signal is emitted when an \a error occurred during scene graph initialization. |
| 4092 | |
| 4093 | You can implement onSceneGraphError(error, message) to handle errors, |
| 4094 | such as graphics context creation failures, in a custom way. |
| 4095 | If no handler is connected to this signal, Quick will print the \a message, |
| 4096 | or show a message box, and terminate the application. |
| 4097 | |
| 4098 | \since 5.3 |
| 4099 | */ |
| 4100 | |
| 4101 | /*! |
| 4102 | \class QQuickCloseEvent |
| 4103 | \internal |
| 4104 | \since 5.1 |
| 4105 | |
| 4106 | \inmodule QtQuick |
| 4107 | |
| 4108 | \brief Notification that a \l QQuickWindow is about to be closed |
| 4109 | */ |
| 4110 | /*! |
| 4111 | \qmltype CloseEvent |
| 4112 | \instantiates QQuickCloseEvent |
| 4113 | \inqmlmodule QtQuick.Window |
| 4114 | \ingroup qtquick-visual |
| 4115 | \brief Notification that a \l Window is about to be closed. |
| 4116 | \since 5.1 |
| 4117 | |
| 4118 | Notification that a window is about to be closed by the windowing system |
| 4119 | (e.g. the user clicked the title bar close button). The CloseEvent contains |
| 4120 | an accepted property which can be set to false to abort closing the window. |
| 4121 | |
| 4122 | \sa QQuickWindow::closing() |
| 4123 | */ |
| 4124 | |
| 4125 | /*! |
| 4126 | \qmlproperty bool CloseEvent::accepted |
| 4127 | |
| 4128 | This property indicates whether the application will allow the user to |
| 4129 | close the window. It is true by default. |
| 4130 | */ |
| 4131 | |
| 4132 | /*! |
| 4133 | \fn void QQuickWindow::closing(QQuickCloseEvent *close) |
| 4134 | \since 5.1 |
| 4135 | |
| 4136 | This signal is emitted when the window receives the event \a close from |
| 4137 | the windowing system. |
| 4138 | |
| 4139 | On \macOs, Qt will create a menu item \c Quit if there is no menu item |
| 4140 | whose text is "quit" or "exit". This menu item calls the \c QCoreApplication::quit |
| 4141 | signal, not the \c QQuickWindow::closing() signal. |
| 4142 | |
| 4143 | \sa {QMenuBar as a Global Menu Bar} |
| 4144 | */ |
| 4145 | |
| 4146 | /*! |
| 4147 | \qmlsignal QtQuick.Window::Window::closing(CloseEvent close) |
| 4148 | \since 5.1 |
| 4149 | |
| 4150 | This signal is emitted when the user tries to close the window. |
| 4151 | |
| 4152 | This signal includes a \a close parameter. The \c {close.accepted} |
| 4153 | property is true by default so that the window is allowed to close; but you |
| 4154 | can implement an \c onClosing handler and set \c {close.accepted = false} if |
| 4155 | you need to do something else before the window can be closed. |
| 4156 | */ |
| 4157 | |
| 4158 | #if QT_CONFIG(opengl) |
| 4159 | /*! |
| 4160 | Sets the render target for this window to be \a fbo. |
| 4161 | |
| 4162 | The specified fbo must be created in the context of the window |
| 4163 | or one that shares with it. |
| 4164 | |
| 4165 | \note |
| 4166 | This function only has an effect when using the default OpenGL scene |
| 4167 | graph adaptation. |
| 4168 | |
| 4169 | \note This function has no effect when running on the RHI graphics abstraction. |
| 4170 | |
| 4171 | \warning |
| 4172 | This function can only be called from the thread doing |
| 4173 | the rendering. |
| 4174 | */ |
| 4175 | |
| 4176 | void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo) |
| 4177 | { |
| 4178 | Q_D(QQuickWindow); |
| 4179 | if (d->rhi) |
| 4180 | return; |
| 4181 | |
| 4182 | if (d->context && QThread::currentThread() != d->context->thread()) { |
| 4183 | qWarning(msg: "QQuickWindow::setRenderTarget: Cannot set render target from outside the rendering thread" ); |
| 4184 | return; |
| 4185 | } |
| 4186 | |
| 4187 | d->renderTarget = fbo; |
| 4188 | if (fbo) { |
| 4189 | d->renderTargetId = fbo->handle(); |
| 4190 | d->renderTargetSize = fbo->size(); |
| 4191 | } else { |
| 4192 | d->renderTargetId = 0; |
| 4193 | d->renderTargetSize = QSize(); |
| 4194 | } |
| 4195 | } |
| 4196 | #endif |
| 4197 | /*! |
| 4198 | \overload |
| 4199 | |
| 4200 | Sets the render target for this window to be an FBO with |
| 4201 | \a fboId and \a size. |
| 4202 | |
| 4203 | The specified FBO must be created in the context of the window |
| 4204 | or one that shares with it. |
| 4205 | |
| 4206 | \note \a fboId can also be set to 0. In this case rendering will target the |
| 4207 | default framebuffer of whichever surface is current when the scenegraph |
| 4208 | renders. \a size must still be valid, specifying the dimensions of the |
| 4209 | surface. |
| 4210 | |
| 4211 | \note |
| 4212 | This function only has an effect when using the default OpenGL scene |
| 4213 | graph adaptation. |
| 4214 | |
| 4215 | \warning |
| 4216 | This function can only be called from the thread doing |
| 4217 | the rendering. |
| 4218 | */ |
| 4219 | |
| 4220 | void QQuickWindow::setRenderTarget(uint fboId, const QSize &size) |
| 4221 | { |
| 4222 | Q_D(QQuickWindow); |
| 4223 | if (d->context && QThread::currentThread() != d->context->thread()) { |
| 4224 | qWarning(msg: "QQuickWindow::setRenderThread: Cannot set render target from outside the rendering thread" ); |
| 4225 | return; |
| 4226 | } |
| 4227 | |
| 4228 | d->renderTargetId = fboId; |
| 4229 | d->renderTargetSize = size; |
| 4230 | |
| 4231 | // Unset any previously set instance... |
| 4232 | d->renderTarget = nullptr; |
| 4233 | } |
| 4234 | |
| 4235 | |
| 4236 | /*! |
| 4237 | Returns the FBO id of the render target when set; otherwise returns 0. |
| 4238 | */ |
| 4239 | uint QQuickWindow::renderTargetId() const |
| 4240 | { |
| 4241 | Q_D(const QQuickWindow); |
| 4242 | return d->renderTargetId; |
| 4243 | } |
| 4244 | |
| 4245 | /*! |
| 4246 | Returns the size of the currently set render target; otherwise returns an empty size. |
| 4247 | */ |
| 4248 | QSize QQuickWindow::renderTargetSize() const |
| 4249 | { |
| 4250 | Q_D(const QQuickWindow); |
| 4251 | return d->renderTargetSize; |
| 4252 | } |
| 4253 | |
| 4254 | |
| 4255 | |
| 4256 | #if QT_CONFIG(opengl) |
| 4257 | /*! |
| 4258 | Returns the render target for this window. |
| 4259 | |
| 4260 | The default is to render to the surface of the window, in which |
| 4261 | case the render target is 0. |
| 4262 | |
| 4263 | \note This function will return nullptr when not using the OpenGL scene |
| 4264 | graph adaptation. |
| 4265 | |
| 4266 | \note This function has no effect and returns nullptr when running on the |
| 4267 | RHI graphics abstraction. |
| 4268 | */ |
| 4269 | QOpenGLFramebufferObject *QQuickWindow::renderTarget() const |
| 4270 | { |
| 4271 | Q_D(const QQuickWindow); |
| 4272 | return d->renderTarget; |
| 4273 | } |
| 4274 | #endif |
| 4275 | |
| 4276 | /*! |
| 4277 | Grabs the contents of the window and returns it as an image. |
| 4278 | |
| 4279 | It is possible to call the grabWindow() function when the window is not |
| 4280 | visible. This requires that the window is \l{QWindow::create()} {created} |
| 4281 | and has a valid size and that no other QQuickWindow instances are rendering |
| 4282 | in the same process. |
| 4283 | |
| 4284 | \warning Calling this function will cause performance problems. |
| 4285 | |
| 4286 | \warning This function can only be called from the GUI thread. |
| 4287 | */ |
| 4288 | QImage QQuickWindow::grabWindow() |
| 4289 | { |
| 4290 | Q_D(QQuickWindow); |
| 4291 | |
| 4292 | if (!isVisible() && !d->renderControl) { |
| 4293 | // backends like software and d3d12 can grab regardless of the window state |
| 4294 | if (d->windowManager && (d->windowManager->flags() & QSGRenderLoop::SupportsGrabWithoutExpose)) |
| 4295 | return d->windowManager->grab(window: this); |
| 4296 | } |
| 4297 | |
| 4298 | if (!isVisible() && !d->renderControl) { |
| 4299 | if (d->rhi) { |
| 4300 | // ### we may need a full offscreen round when non-exposed... |
| 4301 | |
| 4302 | if (d->renderControl) |
| 4303 | return d->renderControl->grab(); |
| 4304 | else if (d->windowManager) |
| 4305 | return d->windowManager->grab(window: this); |
| 4306 | return QImage(); |
| 4307 | } |
| 4308 | |
| 4309 | #if QT_CONFIG(opengl) |
| 4310 | auto openglRenderContext = static_cast<QSGDefaultRenderContext *>(d->context); |
| 4311 | if (!openglRenderContext->openglContext()) { |
| 4312 | if (!handle() || !size().isValid()) { |
| 4313 | qWarning(msg: "QQuickWindow::grabWindow: window must be created and have a valid size" ); |
| 4314 | return QImage(); |
| 4315 | } |
| 4316 | |
| 4317 | QOpenGLContext context; |
| 4318 | context.setFormat(requestedFormat()); |
| 4319 | context.setShareContext(qt_gl_global_share_context()); |
| 4320 | context.create(); |
| 4321 | context.makeCurrent(surface: this); |
| 4322 | QSGDefaultRenderContext::InitParams rcParams; |
| 4323 | rcParams.openGLContext = &context; |
| 4324 | d->context->initialize(params: &rcParams); |
| 4325 | |
| 4326 | d->polishItems(); |
| 4327 | d->syncSceneGraph(); |
| 4328 | d->renderSceneGraph(size: size()); |
| 4329 | |
| 4330 | bool alpha = format().alphaBufferSize() > 0 && color().alpha() < 255; |
| 4331 | QImage image = qt_gl_read_framebuffer(size: size() * effectiveDevicePixelRatio(), alpha_format: alpha, include_alpha: alpha); |
| 4332 | image.setDevicePixelRatio(effectiveDevicePixelRatio()); |
| 4333 | d->cleanupNodesOnShutdown(); |
| 4334 | d->context->invalidate(); |
| 4335 | context.doneCurrent(); |
| 4336 | |
| 4337 | return image; |
| 4338 | } |
| 4339 | #endif |
| 4340 | } |
| 4341 | |
| 4342 | if (d->renderControl) |
| 4343 | return d->renderControl->grab(); |
| 4344 | else if (d->windowManager) |
| 4345 | return d->windowManager->grab(window: this); |
| 4346 | return QImage(); |
| 4347 | } |
| 4348 | |
| 4349 | /*! |
| 4350 | Returns an incubation controller that splices incubation between frames |
| 4351 | for this window. QQuickView automatically installs this controller for you, |
| 4352 | otherwise you will need to install it yourself using \l{QQmlEngine::setIncubationController()}. |
| 4353 | |
| 4354 | The controller is owned by the window and will be destroyed when the window |
| 4355 | is deleted. |
| 4356 | */ |
| 4357 | QQmlIncubationController *QQuickWindow::incubationController() const |
| 4358 | { |
| 4359 | Q_D(const QQuickWindow); |
| 4360 | |
| 4361 | if (!d->windowManager) |
| 4362 | return nullptr; // TODO: make sure that this is safe |
| 4363 | |
| 4364 | if (!d->incubationController) |
| 4365 | d->incubationController = new QQuickWindowIncubationController(d->windowManager); |
| 4366 | return d->incubationController; |
| 4367 | } |
| 4368 | |
| 4369 | |
| 4370 | |
| 4371 | /*! |
| 4372 | \enum QQuickWindow::CreateTextureOption |
| 4373 | |
| 4374 | The CreateTextureOption enums are used to customize a texture is wrapped. |
| 4375 | |
| 4376 | \value TextureHasAlphaChannel The texture has an alpha channel and should |
| 4377 | be drawn using blending. |
| 4378 | |
| 4379 | \value TextureHasMipmaps The texture has mipmaps and can be drawn with |
| 4380 | mipmapping enabled. |
| 4381 | |
| 4382 | \value TextureOwnsGLTexture The texture object owns the texture id and |
| 4383 | will delete the OpenGL texture when the texture object is deleted. |
| 4384 | |
| 4385 | \value TextureCanUseAtlas The image can be uploaded into a texture atlas. |
| 4386 | |
| 4387 | \value TextureIsOpaque The texture will return false for |
| 4388 | QSGTexture::hasAlphaChannel() and will not be blended. This flag was added |
| 4389 | in Qt 5.6. |
| 4390 | |
| 4391 | */ |
| 4392 | |
| 4393 | /*! |
| 4394 | \enum QQuickWindow::SceneGraphError |
| 4395 | |
| 4396 | This enum describes the error in a sceneGraphError() signal. |
| 4397 | |
| 4398 | \value ContextNotAvailable graphics context creation failed. This typically means that |
| 4399 | no suitable OpenGL implementation was found, for example because no graphics drivers |
| 4400 | are installed and so no OpenGL 2 support is present. On mobile and embedded boards |
| 4401 | that use OpenGL ES such an error is likely to indicate issues in the windowing system |
| 4402 | integration and possibly an incorrect configuration of Qt. |
| 4403 | |
| 4404 | \since 5.3 |
| 4405 | */ |
| 4406 | |
| 4407 | /*! |
| 4408 | \enum QQuickWindow::TextRenderType |
| 4409 | \since 5.10 |
| 4410 | |
| 4411 | This enum describes the default render type of text-like elements in Qt |
| 4412 | Quick (\l Text, \l TextInput, etc.). |
| 4413 | |
| 4414 | Select NativeTextRendering if you prefer text to look native on the target |
| 4415 | platform and do not require advanced features such as transformation of the |
| 4416 | text. Using such features in combination with the NativeTextRendering |
| 4417 | render type will lend poor and sometimes pixelated results. |
| 4418 | |
| 4419 | \value QtTextRendering Use Qt's own rasterization algorithm. |
| 4420 | |
| 4421 | \value NativeTextRendering Use the operating system's native rasterizer for text. |
| 4422 | */ |
| 4423 | |
| 4424 | /*! |
| 4425 | \fn void QQuickWindow::beforeSynchronizing() |
| 4426 | |
| 4427 | This signal is emitted before the scene graph is synchronized with the QML state. |
| 4428 | |
| 4429 | This signal can be used to do any preparation required before calls to |
| 4430 | QQuickItem::updatePaintNode(). |
| 4431 | |
| 4432 | The OpenGL context used for rendering the scene graph will be bound at this point. |
| 4433 | |
| 4434 | \warning This signal is emitted from the scene graph rendering thread. If your |
| 4435 | slot function needs to finish before execution continues, you must make sure that |
| 4436 | the connection is direct (see Qt::ConnectionType). |
| 4437 | |
| 4438 | \warning Make very sure that a signal handler for beforeSynchronizing leaves the GL |
| 4439 | context in the same state as it was when the signal handler was entered. Failing to |
| 4440 | do so can result in the scene not rendering properly. |
| 4441 | |
| 4442 | \sa resetOpenGLState() |
| 4443 | */ |
| 4444 | |
| 4445 | /*! |
| 4446 | \qmlsignal QtQuick.Window::Window::beforeSynchronizing() |
| 4447 | \internal |
| 4448 | */ |
| 4449 | |
| 4450 | /*! |
| 4451 | \fn void QQuickWindow::afterSynchronizing() |
| 4452 | |
| 4453 | This signal is emitted after the scene graph is synchronized with the QML state. |
| 4454 | |
| 4455 | This signal can be used to do preparation required after calls to |
| 4456 | QQuickItem::updatePaintNode(), while the GUI thread is still locked. |
| 4457 | |
| 4458 | The graphics context used for rendering the scene graph will be bound at this point. |
| 4459 | |
| 4460 | \warning This signal is emitted from the scene graph rendering thread. If your |
| 4461 | slot function needs to finish before execution continues, you must make sure that |
| 4462 | the connection is direct (see Qt::ConnectionType). |
| 4463 | |
| 4464 | \warning When using the OpenGL adaptation, make sure that a signal handler for |
| 4465 | afterSynchronizing leaves the OpenGL context in the same state as it was when the |
| 4466 | signal handler was entered. Failing to do so can result in the scene not rendering |
| 4467 | properly. |
| 4468 | |
| 4469 | \since 5.3 |
| 4470 | \sa resetOpenGLState() |
| 4471 | */ |
| 4472 | |
| 4473 | /*! |
| 4474 | \qmlsignal QtQuick.Window::Window::afterSynchronizing() |
| 4475 | \internal |
| 4476 | \since 5.3 |
| 4477 | */ |
| 4478 | |
| 4479 | /*! |
| 4480 | \fn void QQuickWindow::beforeRendering() |
| 4481 | |
| 4482 | This signal is emitted before the scene starts rendering. |
| 4483 | |
| 4484 | Combined with the modes for clearing the background, this option |
| 4485 | can be used to paint using raw OpenGL under QML content. |
| 4486 | |
| 4487 | The OpenGL context used for rendering the scene graph will be bound |
| 4488 | at this point. |
| 4489 | |
| 4490 | When using the RHI, the signal is emitted after the preparations for the |
| 4491 | frame have been done, meaning there is a command buffer in recording mode, |
| 4492 | where applicable. If desired, the slot function connected to this signal |
| 4493 | can query native resources like the command before via |
| 4494 | QSGRendererInterface. Note however that the recording of the main render |
| 4495 | pass is not yet started at this point and it is not possible to add |
| 4496 | commands within that pass. Starting a pass means clearing the color, depth, |
| 4497 | and stencil buffers so it is not possible to achieve an underlay type of |
| 4498 | rendering by just connecting to this signal. Rather, connect to |
| 4499 | beforeRenderPassRecording(). However, connecting to this signal is still |
| 4500 | important if the recording of copy type of commands is desired since those |
| 4501 | cannot be enqueued within a render pass. |
| 4502 | |
| 4503 | \warning This signal is emitted from the scene graph rendering thread. If your |
| 4504 | slot function needs to finish before execution continues, you must make sure that |
| 4505 | the connection is direct (see Qt::ConnectionType). |
| 4506 | |
| 4507 | \warning Make very sure that a signal handler for beforeRendering leaves the OpenGL |
| 4508 | context in the same state as it was when the signal handler was entered. Failing to |
| 4509 | do so can result in the scene not rendering properly. |
| 4510 | |
| 4511 | \sa resetOpenGLState() |
| 4512 | */ |
| 4513 | |
| 4514 | /*! |
| 4515 | \qmlsignal QtQuick.Window::Window::beforeRendering() |
| 4516 | \internal |
| 4517 | */ |
| 4518 | |
| 4519 | /*! |
| 4520 | \fn void QQuickWindow::afterRendering() |
| 4521 | |
| 4522 | This signal is emitted after the scene has completed rendering, before swapbuffers is called. |
| 4523 | |
| 4524 | This signal can be used to paint using raw OpenGL on top of QML content, |
| 4525 | or to do screen scraping of the current frame buffer. |
| 4526 | |
| 4527 | The OpenGL context used for rendering the scene graph will be bound at this point. |
| 4528 | |
| 4529 | When using the RHI, the signal is emitted after scene graph has added its |
| 4530 | commands to the command buffer, which is not yet submitted to the graphics |
| 4531 | queue. If desired, the slot function connected to this signal can query |
| 4532 | native resources, like the command buffer, before via QSGRendererInterface. |
| 4533 | Note however that the render pass (or passes) are already recorded at this |
| 4534 | point and it is not possible to add more commands within the scenegraph's |
| 4535 | pass. Instead, use afterRenderPassRecording() for that. This signal has |
| 4536 | therefore limited use and is rarely needed in an RHI-based setup. Rather, |
| 4537 | it is the combination of beforeRendering() + beforeRenderPassRecording() or |
| 4538 | beforeRendering() + afterRenderPassRecording() that is typically used to |
| 4539 | achieve under- or overlaying of the custom rendering. |
| 4540 | |
| 4541 | \warning This signal is emitted from the scene graph rendering thread. If your |
| 4542 | slot function needs to finish before execution continues, you must make sure that |
| 4543 | the connection is direct (see Qt::ConnectionType). |
| 4544 | |
| 4545 | \warning Make very sure that a signal handler for afterRendering() leaves the OpenGL |
| 4546 | context in the same state as it was when the signal handler was entered. Failing to |
| 4547 | do so can result in the scene not rendering properly. |
| 4548 | |
| 4549 | \sa resetOpenGLState() |
| 4550 | */ |
| 4551 | |
| 4552 | /*! |
| 4553 | \qmlsignal QtQuick.Window::Window::afterRendering() |
| 4554 | \internal |
| 4555 | */ |
| 4556 | |
| 4557 | /*! |
| 4558 | \fn void QQuickWindow::beforeRenderPassRecording() |
| 4559 | |
| 4560 | This signal is emitted before the scenegraph starts recording commands for |
| 4561 | the main render pass. (Layers have their own passes and are fully recorded |
| 4562 | by the time this signal is emitted.) The render pass is already active on |
| 4563 | the command buffer when the signal is emitted. |
| 4564 | |
| 4565 | This signal is applicable when using the RHI graphics abstraction with the |
| 4566 | scenegraph. It is emitted later than beforeRendering() and it guarantees |
| 4567 | that not just the frame, but also the recording of the scenegraph's main |
| 4568 | render pass is active. This allows inserting commands without having to |
| 4569 | generate an entire, separate render pass (which would typically clear the |
| 4570 | attached images). The native graphics objects can be queried via |
| 4571 | QSGRendererInterface. |
| 4572 | |
| 4573 | When not running with the RHI (and using OpenGL directly), the signal is |
| 4574 | emitted after the renderer has cleared the render target. This makes it |
| 4575 | possible to create applications that function identically both with and |
| 4576 | without the RHI. |
| 4577 | |
| 4578 | \note Resource updates (uploads, copies) typically cannot be enqueued from |
| 4579 | within a render pass. Therefore, more complex user rendering will need to |
| 4580 | connect to both beforeRendering() and this signal. |
| 4581 | |
| 4582 | \warning This signal is emitted from the scene graph rendering thread. If your |
| 4583 | slot function needs to finish before execution continues, you must make sure that |
| 4584 | the connection is direct (see Qt::ConnectionType). |
| 4585 | |
| 4586 | \since 5.14 |
| 4587 | */ |
| 4588 | |
| 4589 | /*! |
| 4590 | \qmlsignal QtQuick.Window::Window::beforeRenderPassRecording() |
| 4591 | \internal |
| 4592 | \since 5.14 |
| 4593 | */ |
| 4594 | |
| 4595 | /*! |
| 4596 | \fn void QQuickWindow::afterRenderPassRecording() |
| 4597 | |
| 4598 | This signal is emitted after the scenegraph has recorded the commands for |
| 4599 | its main render pass, but the pass is not yet finalized on the command |
| 4600 | buffer. |
| 4601 | |
| 4602 | This signal is applicable when using the RHI graphics abstraction with the |
| 4603 | scenegraph. It is emitted earlier than afterRendering() and it guarantees |
| 4604 | that not just the frame, but also the recording of the scenegraph's main |
| 4605 | render pass is still active. This allows inserting commands without having |
| 4606 | to generate an entire, separate render pass (which would typically clear |
| 4607 | the attached images). The native graphics objects can be queried via |
| 4608 | QSGRendererInterface. |
| 4609 | |
| 4610 | When not running with the RHI (and using OpenGL directly), the signal is |
| 4611 | emitted after the renderer has finished its rendering, but before |
| 4612 | afterRendering(). This makes it possible to create applications that |
| 4613 | function identically both with and without the RHI. |
| 4614 | |
| 4615 | \note Resource updates (uploads, copies) typically cannot be enqueued from |
| 4616 | within a render pass. Therefore, more complex user rendering will need to |
| 4617 | connect to both beforeRendering() and this signal. |
| 4618 | |
| 4619 | \warning This signal is emitted from the scene graph rendering thread. If your |
| 4620 | slot function needs to finish before execution continues, you must make sure that |
| 4621 | the connection is direct (see Qt::ConnectionType). |
| 4622 | |
| 4623 | \since 5.14 |
| 4624 | */ |
| 4625 | |
| 4626 | /*! |
| 4627 | \qmlsignal QtQuick.Window::Window::afterRenderPassRecording() |
| 4628 | \internal |
| 4629 | \since 5.14 |
| 4630 | */ |
| 4631 | |
| 4632 | /*! |
| 4633 | \fn void QQuickWindow::afterAnimating() |
| 4634 | |
| 4635 | This signal is emitted on the GUI thread before requesting the render thread to |
| 4636 | perform the synchronization of the scene graph. |
| 4637 | |
| 4638 | Unlike the other similar signals, this one is emitted on the GUI thread |
| 4639 | instead of the render thread. It can be used to synchronize external |
| 4640 | animation systems with the QML content. At the same time this means that |
| 4641 | this signal is not suitable for triggering graphics operations. |
| 4642 | |
| 4643 | \since 5.3 |
| 4644 | */ |
| 4645 | |
| 4646 | /*! |
| 4647 | \qmlsignal QtQuick.Window::Window::afterAnimating() |
| 4648 | |
| 4649 | This signal is emitted on the GUI thread before requesting the render thread to |
| 4650 | perform the synchronization of the scene graph. |
| 4651 | |
| 4652 | You can implement onAfterAnimating to do additional processing after each animation step. |
| 4653 | |
| 4654 | \since 5.3 |
| 4655 | */ |
| 4656 | |
| 4657 | /*! |
| 4658 | \fn void QQuickWindow::openglContextCreated(QOpenGLContext *context) |
| 4659 | |
| 4660 | This signal is emitted on the GUI thread when the OpenGL \a context |
| 4661 | for this window is created, before it is made current. |
| 4662 | |
| 4663 | Some implementations will share the same OpenGL context between |
| 4664 | multiple QQuickWindow instances. The openglContextCreated() signal |
| 4665 | will in this case only be emitted for the first window, when the |
| 4666 | OpenGL context is actually created. |
| 4667 | |
| 4668 | QQuickWindow::openglContext() will still return 0 for this window |
| 4669 | until after the QQuickWindow::sceneGraphInitialized() has been |
| 4670 | emitted. |
| 4671 | |
| 4672 | \note |
| 4673 | This signal will only be emmited when using the default OpenGL scene |
| 4674 | graph adaptation. |
| 4675 | |
| 4676 | \since 5.3 |
| 4677 | */ |
| 4678 | |
| 4679 | /*! |
| 4680 | \qmlsignal QtQuick.Window::Window::openglContextCreated() |
| 4681 | \internal |
| 4682 | \since 5.3 |
| 4683 | */ |
| 4684 | |
| 4685 | /*! |
| 4686 | \fn void QQuickWindow::sceneGraphAboutToStop() |
| 4687 | |
| 4688 | This signal is emitted on the render thread when the scene graph is |
| 4689 | about to stop rendering. This happens usually because the window |
| 4690 | has been hidden. |
| 4691 | |
| 4692 | Applications may use this signal to release resources, but should be |
| 4693 | prepared to reinstantiated them again fast. The scene graph and the |
| 4694 | graphics context are not released at this time. |
| 4695 | |
| 4696 | \warning This signal is emitted from the scene graph rendering thread. If your |
| 4697 | slot function needs to finish before execution continues, you must make sure that |
| 4698 | the connection is direct (see Qt::ConnectionType). |
| 4699 | |
| 4700 | \warning Make very sure that a signal handler for sceneGraphAboutToStop() leaves the |
| 4701 | graphics context in the same state as it was when the signal handler was entered. |
| 4702 | Failing to do so can result in the scene not rendering properly. |
| 4703 | |
| 4704 | \sa sceneGraphInvalidated(), resetOpenGLState() |
| 4705 | \since 5.3 |
| 4706 | */ |
| 4707 | |
| 4708 | /*! |
| 4709 | \qmlsignal QtQuick.Window::Window::sceneGraphAboutToStop() |
| 4710 | \internal |
| 4711 | \since 5.3 |
| 4712 | */ |
| 4713 | |
| 4714 | /*! |
| 4715 | Sets whether the scene graph rendering of QML should clear the color buffer |
| 4716 | before it starts rendering to \a enabled. |
| 4717 | |
| 4718 | By disabling clearing of the color buffer, it is possible to render OpengGL content |
| 4719 | under the scene graph. |
| 4720 | |
| 4721 | The color buffer is cleared by default. |
| 4722 | |
| 4723 | \warning This flag is ignored completely when running with the RHI graphics |
| 4724 | abstraction instead of using OpenGL directly. As explicit clear commands |
| 4725 | simply do not exist in some modern APIs, the scene graph cannot offer this |
| 4726 | flexibility anymore. The images associated with a render target will always |
| 4727 | get cleared when a render pass starts. As a solution, an alternative to |
| 4728 | disabling scene graph issued clears is provided in form of the |
| 4729 | beforeRenderPassRecording() signal. |
| 4730 | |
| 4731 | \sa beforeRendering(), beforeRenderPassRecording() |
| 4732 | */ |
| 4733 | |
| 4734 | void QQuickWindow::setClearBeforeRendering(bool enabled) |
| 4735 | { |
| 4736 | Q_D(QQuickWindow); |
| 4737 | d->clearBeforeRendering = enabled; |
| 4738 | } |
| 4739 | |
| 4740 | |
| 4741 | |
| 4742 | /*! |
| 4743 | Returns whether clearing of the color buffer is done before rendering or not. |
| 4744 | */ |
| 4745 | |
| 4746 | bool QQuickWindow::clearBeforeRendering() const |
| 4747 | { |
| 4748 | Q_D(const QQuickWindow); |
| 4749 | return d->clearBeforeRendering; |
| 4750 | } |
| 4751 | |
| 4752 | /*! |
| 4753 | \overload |
| 4754 | */ |
| 4755 | |
| 4756 | QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const |
| 4757 | { |
| 4758 | return createTextureFromImage(image, options: {}); |
| 4759 | } |
| 4760 | |
| 4761 | |
| 4762 | /*! |
| 4763 | Creates a new QSGTexture from the supplied \a image. If the image has an |
| 4764 | alpha channel, the corresponding texture will have an alpha channel. |
| 4765 | |
| 4766 | The caller of the function is responsible for deleting the returned texture. |
| 4767 | For example whe using the OpenGL adaptation the actual OpenGL texture will |
| 4768 | be deleted when the texture object is deleted. |
| 4769 | |
| 4770 | When \a options contains TextureCanUseAtlas, the engine may put the image |
| 4771 | into a texture atlas. Textures in an atlas need to rely on |
| 4772 | QSGTexture::normalizedTextureSubRect() for their geometry and will not |
| 4773 | support QSGTexture::Repeat. Other values from CreateTextureOption are |
| 4774 | ignored. |
| 4775 | |
| 4776 | When \a options contains TextureIsOpaque, the engine will create an RGB |
| 4777 | texture which returns false for QSGTexture::hasAlphaChannel(). Opaque |
| 4778 | textures will in most cases be faster to render. When this flag is not set, |
| 4779 | the texture will have an alpha channel based on the image's format. |
| 4780 | |
| 4781 | When \a options contains TextureHasMipmaps, the engine will create a |
| 4782 | texture which can use mipmap filtering. Mipmapped textures can not be in |
| 4783 | an atlas. |
| 4784 | |
| 4785 | When using the OpenGL adaptation, the returned texture will be using |
| 4786 | \c GL_TEXTURE_2D as texture target and \c GL_RGBA as internal format. |
| 4787 | Reimplement QSGTexture to create textures with different parameters. |
| 4788 | |
| 4789 | \warning This function will return 0 if the scene graph has not yet been |
| 4790 | initialized. |
| 4791 | |
| 4792 | \warning The returned texture is not memory managed by the scene graph and |
| 4793 | must be explicitly deleted by the caller on the rendering thread. |
| 4794 | This is achieved by deleting the texture from a QSGNode destructor |
| 4795 | or by using deleteLater() in the case where the texture already has affinity |
| 4796 | to the rendering thread. |
| 4797 | |
| 4798 | This function can be called from any thread. |
| 4799 | |
| 4800 | \sa sceneGraphInitialized(), QSGTexture |
| 4801 | */ |
| 4802 | |
| 4803 | QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateTextureOptions options) const |
| 4804 | { |
| 4805 | Q_D(const QQuickWindow); |
| 4806 | if (!isSceneGraphInitialized()) // check both for d->context and d->context->isValid() |
| 4807 | return nullptr; |
| 4808 | uint flags = 0; |
| 4809 | if (options & TextureCanUseAtlas) flags |= QSGRenderContext::CreateTexture_Atlas; |
| 4810 | if (options & TextureHasMipmaps) flags |= QSGRenderContext::CreateTexture_Mipmap; |
| 4811 | if (!(options & TextureIsOpaque)) flags |= QSGRenderContext::CreateTexture_Alpha; |
| 4812 | return d->context->createTexture(image, flags); |
| 4813 | } |
| 4814 | |
| 4815 | |
| 4816 | #if QT_DEPRECATED_SINCE(5, 15) |
| 4817 | /*! |
| 4818 | Creates a new QSGTexture object from an existing OpenGL texture \a id and \a size. |
| 4819 | |
| 4820 | The caller of the function is responsible for deleting the returned texture. |
| 4821 | |
| 4822 | The returned texture will be using \c GL_TEXTURE_2D as texture target and |
| 4823 | assumes that internal format is \c {GL_RGBA}. Reimplement QSGTexture to |
| 4824 | create textures with different parameters. |
| 4825 | |
| 4826 | Use \a options to customize the texture attributes. The TextureUsesAtlas |
| 4827 | option is ignored. |
| 4828 | |
| 4829 | \warning This function will return null if the scenegraph has not yet been |
| 4830 | initialized or OpenGL is not in use. |
| 4831 | |
| 4832 | \note This function only has an effect when using the default OpenGL scene graph |
| 4833 | adaptation. |
| 4834 | |
| 4835 | \note This function has no effect when running on the RHI graphics |
| 4836 | abstraction. Use createTextureFromNativeObject() instead. |
| 4837 | |
| 4838 | \obsolete |
| 4839 | |
| 4840 | \sa sceneGraphInitialized(), QSGTexture |
| 4841 | */ |
| 4842 | QSGTexture *QQuickWindow::createTextureFromId(uint id, const QSize &size, CreateTextureOptions options) const |
| 4843 | { |
| 4844 | #if QT_CONFIG(opengl) |
| 4845 | Q_D(const QQuickWindow); |
| 4846 | if (!d->rhi) { |
| 4847 | if (openglContext()) { |
| 4848 | QSGPlainTexture *texture = new QSGPlainTexture(); |
| 4849 | texture->setTextureId(id); |
| 4850 | texture->setHasAlphaChannel(options & TextureHasAlphaChannel); |
| 4851 | texture->setOwnsTexture(options & TextureOwnsGLTexture); |
| 4852 | texture->setTextureSize(size); |
| 4853 | return texture; |
| 4854 | } |
| 4855 | } else { |
| 4856 | qWarning(msg: "createTextureFromId() must not be called when running on the RHI. " |
| 4857 | "Use createTextureFromNativeObject() instead." ); |
| 4858 | } |
| 4859 | #else |
| 4860 | Q_UNUSED(id) |
| 4861 | Q_UNUSED(size) |
| 4862 | Q_UNUSED(options) |
| 4863 | #endif |
| 4864 | return nullptr; |
| 4865 | } |
| 4866 | #endif |
| 4867 | |
| 4868 | /*! |
| 4869 | \enum QQuickWindow::NativeObjectType |
| 4870 | \since 5.14 |
| 4871 | |
| 4872 | Specifies the type of the native object passed to functions such as |
| 4873 | createTextureFromNativeObject(). |
| 4874 | |
| 4875 | \value NativeObjectTexture The native object is a 2D texture (OpenGL, |
| 4876 | Direct3D 11, Metal) or image (Vulkan). |
| 4877 | */ |
| 4878 | |
| 4879 | /*! |
| 4880 | Creates a new QSGTexture object from an existing native object. |
| 4881 | |
| 4882 | The native object is wrapped, but not owned, by the resulting QSGTexture. |
| 4883 | The caller of the function is responsible for deleting the returned |
| 4884 | QSGTexture, but that will not destroy the underlying native object. |
| 4885 | |
| 4886 | \a type specifies the type of the object. In practice the type is |
| 4887 | NativeObjectTexture, indicating that the native object is a texture or |
| 4888 | image of the underlying graphics API. Other types may be introduced in the |
| 4889 | future. |
| 4890 | |
| 4891 | This function is currently suitable for 2D RGBA textures only. |
| 4892 | |
| 4893 | Unlike createTextureFromId(), this function supports both direct OpenGL |
| 4894 | usage and the RHI abstracted rendering path. |
| 4895 | |
| 4896 | \warning This function will return null if the scenegraph has not yet been |
| 4897 | initialized. |
| 4898 | |
| 4899 | Use \a options to customize the texture attributes. Only the |
| 4900 | TextureHasAlphaChannel and TextureHasMipmaps are taken into account here. |
| 4901 | |
| 4902 | \warning Unlike createTextureFromId(), this function never takes ownership |
| 4903 | of the native object, and the TextureOwnsGLTexture flag is ignored. |
| 4904 | |
| 4905 | \a size specifies the size in pixels. |
| 4906 | |
| 4907 | \a nativeObjectPtr is a pointer to the native object handle. With OpenGL, |
| 4908 | the native handle is a GLuint value, so \a nativeObjectPtr is then a |
| 4909 | pointer to a GLuint. With Vulkan, the native handle is a VkImage, so \a |
| 4910 | nativeObjectPtr is a pointer to a VkImage. With Direct3D 11 and Metal \a |
| 4911 | nativeObjectPtr is a pointer to a ID3D11Texture2D or MTLTexture pointer. |
| 4912 | |
| 4913 | \note Pay attention to the fact that \a nativeObjectPtr is always a pointer |
| 4914 | to the native texture handle type, even if the native type itself is a |
| 4915 | pointer. |
| 4916 | |
| 4917 | \a nativeLayout is only used for APIs like Vulkan. When applicable, it must |
| 4918 | specify the current image layout, such as, a VkImageLayout value. |
| 4919 | |
| 4920 | \sa sceneGraphInitialized(), QSGTexture, QSGTexture::nativeTexture() |
| 4921 | |
| 4922 | \since 5.14 |
| 4923 | */ |
| 4924 | QSGTexture *QQuickWindow::createTextureFromNativeObject(NativeObjectType type, |
| 4925 | const void *nativeObjectPtr, |
| 4926 | int nativeLayout, |
| 4927 | const QSize &size, |
| 4928 | CreateTextureOptions options) const |
| 4929 | { |
| 4930 | if (type != NativeObjectTexture) { |
| 4931 | qWarning(msg: "createTextureFromNativeObject: only textures are supported" ); |
| 4932 | return nullptr; |
| 4933 | } |
| 4934 | |
| 4935 | #if QT_CONFIG(opengl) /* || QT_CONFIG(vulkan) || defined(Q_OS_WIN) || defined(Q_OS_DARWIN) */ |
| 4936 | Q_D(const QQuickWindow); |
| 4937 | if (d->rhi) { |
| 4938 | QSGPlainTexture *texture = new QSGPlainTexture; |
| 4939 | texture->setTextureFromNativeObject(rhi: d->rhi, type, nativeObjectPtr, nativeLayout, |
| 4940 | size, mipmap: options.testFlag(flag: TextureHasMipmaps)); |
| 4941 | texture->setHasAlphaChannel(options & TextureHasAlphaChannel); |
| 4942 | // note that the QRhiTexture does not (and cannot) own the native object |
| 4943 | texture->setOwnsTexture(true); // texture meaning the QRhiTexture here, not the native object |
| 4944 | texture->setTextureSize(size); |
| 4945 | return texture; |
| 4946 | } else if (openglContext()) { |
| 4947 | QSGPlainTexture *texture = new QSGPlainTexture; |
| 4948 | texture->setTextureId(*reinterpret_cast<const uint *>(nativeObjectPtr)); |
| 4949 | texture->setHasAlphaChannel(options & TextureHasAlphaChannel); |
| 4950 | texture->setOwnsTexture(options & TextureOwnsGLTexture); |
| 4951 | texture->setTextureSize(size); |
| 4952 | return texture; |
| 4953 | } |
| 4954 | #else |
| 4955 | Q_UNUSED(nativeObjectPtr); |
| 4956 | Q_UNUSED(nativeLayout); |
| 4957 | Q_UNUSED(size); |
| 4958 | Q_UNUSED(options); |
| 4959 | #endif |
| 4960 | |
| 4961 | return nullptr; |
| 4962 | } |
| 4963 | |
| 4964 | /*! |
| 4965 | \qmlproperty color Window::color |
| 4966 | |
| 4967 | The background color for the window. |
| 4968 | |
| 4969 | Setting this property is more efficient than using a separate Rectangle. |
| 4970 | */ |
| 4971 | |
| 4972 | /*! |
| 4973 | \property QQuickWindow::color |
| 4974 | \brief The color used to clear the OpenGL context. |
| 4975 | |
| 4976 | Setting the clear color has no effect when clearing is disabled. |
| 4977 | By default, the clear color is white. |
| 4978 | |
| 4979 | \sa setClearBeforeRendering(), setDefaultAlphaBuffer() |
| 4980 | */ |
| 4981 | |
| 4982 | void QQuickWindow::setColor(const QColor &color) |
| 4983 | { |
| 4984 | Q_D(QQuickWindow); |
| 4985 | if (color == d->clearColor) |
| 4986 | return; |
| 4987 | |
| 4988 | if (color.alpha() != d->clearColor.alpha()) { |
| 4989 | QSurfaceFormat fmt = requestedFormat(); |
| 4990 | if (color.alpha() < 255) |
| 4991 | fmt.setAlphaBufferSize(8); |
| 4992 | else |
| 4993 | fmt.setAlphaBufferSize(-1); |
| 4994 | setFormat(fmt); |
| 4995 | } |
| 4996 | d->clearColor = color; |
| 4997 | emit colorChanged(color); |
| 4998 | update(); |
| 4999 | } |
| 5000 | |
| 5001 | QColor QQuickWindow::color() const |
| 5002 | { |
| 5003 | return d_func()->clearColor; |
| 5004 | } |
| 5005 | |
| 5006 | /*! |
| 5007 | \brief Returns whether to use alpha transparency on newly created windows. |
| 5008 | |
| 5009 | \since 5.1 |
| 5010 | \sa setDefaultAlphaBuffer() |
| 5011 | */ |
| 5012 | bool QQuickWindow::hasDefaultAlphaBuffer() |
| 5013 | { |
| 5014 | return QQuickWindowPrivate::defaultAlphaBuffer; |
| 5015 | } |
| 5016 | |
| 5017 | /*! |
| 5018 | \brief \a useAlpha specifies whether to use alpha transparency on newly created windows. |
| 5019 | \since 5.1 |
| 5020 | |
| 5021 | In any application which expects to create translucent windows, it's necessary to set |
| 5022 | this to true before creating the first QQuickWindow. The default value is false. |
| 5023 | |
| 5024 | \sa hasDefaultAlphaBuffer() |
| 5025 | */ |
| 5026 | void QQuickWindow::setDefaultAlphaBuffer(bool useAlpha) |
| 5027 | { |
| 5028 | QQuickWindowPrivate::defaultAlphaBuffer = useAlpha; |
| 5029 | } |
| 5030 | #if QT_CONFIG(opengl) |
| 5031 | /*! |
| 5032 | \since 5.2 |
| 5033 | |
| 5034 | Call this function to reset the OpenGL context its default state. |
| 5035 | |
| 5036 | The scene graph uses the OpenGL context and will both rely on and |
| 5037 | clobber its state. When mixing raw OpenGL commands with scene |
| 5038 | graph rendering, this function provides a convenient way of |
| 5039 | resetting the OpenGL context state back to its default values. |
| 5040 | |
| 5041 | This function does not touch state in the fixed-function pipeline. |
| 5042 | |
| 5043 | This function does not clear the color, depth and stencil buffers. Use |
| 5044 | QQuickWindow::setClearBeforeRendering to control clearing of the color |
| 5045 | buffer. The depth and stencil buffer might be clobbered by the scene |
| 5046 | graph renderer. Clear these manually on demand. |
| 5047 | |
| 5048 | \note This function only has an effect when using the default OpenGL scene graph |
| 5049 | adaptation. |
| 5050 | |
| 5051 | \note This function will only reset the OpenGL context in relation to what has been changed |
| 5052 | internally as part of the OpenGL scene graph. It does not reset anything that has been changed |
| 5053 | externally such as direct OpenGL calls done inside the application code if those same calls are |
| 5054 | not used internally. |
| 5055 | |
| 5056 | \note This function has no effect when running on the RHI graphics |
| 5057 | abstraction and the underlying RHI backend is not OpenGL. |
| 5058 | |
| 5059 | \sa QQuickWindow::beforeRendering(), beginExternalCommands(), endExternalCommands() |
| 5060 | */ |
| 5061 | void QQuickWindow::resetOpenGLState() |
| 5062 | { |
| 5063 | Q_D(QQuickWindow); |
| 5064 | |
| 5065 | if (!openglContext()) |
| 5066 | return; |
| 5067 | |
| 5068 | QOpenGLContext *ctx = openglContext(); |
| 5069 | QOpenGLFunctions *gl = ctx->functions(); |
| 5070 | |
| 5071 | gl->glBindBuffer(GL_ARRAY_BUFFER, buffer: 0); |
| 5072 | gl->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: 0); |
| 5073 | |
| 5074 | if (!d->vaoHelper) |
| 5075 | d->vaoHelper = new QOpenGLVertexArrayObjectHelper(ctx); |
| 5076 | if (d->vaoHelper->isValid()) |
| 5077 | d->vaoHelper->glBindVertexArray(array: 0); |
| 5078 | |
| 5079 | if (ctx->isOpenGLES() || (gl->openGLFeatures() & QOpenGLFunctions::FixedFunctionPipeline)) { |
| 5080 | int maxAttribs; |
| 5081 | gl->glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, params: &maxAttribs); |
| 5082 | for (int i=0; i<maxAttribs; ++i) { |
| 5083 | gl->glVertexAttribPointer(indx: i, size: 4, GL_FLOAT, GL_FALSE, stride: 0, ptr: nullptr); |
| 5084 | gl->glDisableVertexAttribArray(index: i); |
| 5085 | } |
| 5086 | } |
| 5087 | |
| 5088 | gl->glActiveTexture(GL_TEXTURE0); |
| 5089 | gl->glBindTexture(GL_TEXTURE_2D, texture: 0); |
| 5090 | |
| 5091 | gl->glDisable(GL_DEPTH_TEST); |
| 5092 | gl->glDisable(GL_STENCIL_TEST); |
| 5093 | gl->glDisable(GL_SCISSOR_TEST); |
| 5094 | |
| 5095 | gl->glColorMask(red: true, green: true, blue: true, alpha: true); |
| 5096 | gl->glClearColor(red: 0, green: 0, blue: 0, alpha: 0); |
| 5097 | |
| 5098 | gl->glDepthMask(flag: true); |
| 5099 | gl->glDepthFunc(GL_LESS); |
| 5100 | gl->glClearDepthf(depth: 1); |
| 5101 | |
| 5102 | gl->glStencilMask(mask: 0xff); |
| 5103 | gl->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); |
| 5104 | gl->glStencilFunc(GL_ALWAYS, ref: 0, mask: 0xff); |
| 5105 | |
| 5106 | gl->glDisable(GL_BLEND); |
| 5107 | gl->glBlendFunc(GL_ONE, GL_ZERO); |
| 5108 | |
| 5109 | gl->glUseProgram(program: 0); |
| 5110 | |
| 5111 | QOpenGLFramebufferObject::bindDefault(); |
| 5112 | } |
| 5113 | #endif |
| 5114 | |
| 5115 | /*! |
| 5116 | \struct QQuickWindow::GraphicsStateInfo |
| 5117 | \inmodule QtQuick |
| 5118 | \since 5.14 |
| 5119 | |
| 5120 | \brief Describes some of the RHI's graphics state at the point of a |
| 5121 | \l{QQuickWindow::beginExternalCommands()}{beginExternalCommands()} call. |
| 5122 | */ |
| 5123 | |
| 5124 | /*! |
| 5125 | \return a reference to a GraphicsStateInfo struct describing some of the |
| 5126 | RHI's internal state, in particular, the double or tripple buffering status |
| 5127 | of the backend (such as, the Vulkan or Metal integrations). This is |
| 5128 | relevant when the underlying graphics APIs is Vulkan or Metal, and the |
| 5129 | external rendering code wishes to perform double or tripple buffering of |
| 5130 | its own often-changing resources, such as, uniform buffers, in order to |
| 5131 | avoid stalling the pipeline. |
| 5132 | */ |
| 5133 | const QQuickWindow::GraphicsStateInfo &QQuickWindow::graphicsStateInfo() |
| 5134 | { |
| 5135 | Q_D(QQuickWindow); |
| 5136 | if (d->rhi) { |
| 5137 | d->rhiStateInfo.currentFrameSlot = d->rhi->currentFrameSlot(); |
| 5138 | d->rhiStateInfo.framesInFlight = d->rhi->resourceLimit(limit: QRhi::FramesInFlight); |
| 5139 | } |
| 5140 | return d->rhiStateInfo; |
| 5141 | } |
| 5142 | |
| 5143 | /*! |
| 5144 | When mixing raw graphics (OpenGL, Vulkan, Metal, etc.) commands with scene |
| 5145 | graph rendering, it is necessary to call this function before recording |
| 5146 | commands to the command buffer used by the scene graph to render its main |
| 5147 | render pass. This is to avoid clobbering state. |
| 5148 | |
| 5149 | In practice this function is often called from a slot connected to the |
| 5150 | beforeRenderPassRecording() or afterRenderPassRecording() signals. |
| 5151 | |
| 5152 | The function does not need to be called when recording commands to the |
| 5153 | application's own command buffer (such as, a VkCommandBuffer or |
| 5154 | MTLCommandBuffer + MTLRenderCommandEncoder created and managed by the |
| 5155 | application, not retrieved from the scene graph). With graphics APIs where |
| 5156 | no native command buffer concept is exposed (OpenGL, Direct 3D 11), |
| 5157 | beginExternalCommands() and endExternalCommands() together provide a |
| 5158 | replacement for resetOpenGLState(). |
| 5159 | |
| 5160 | Calling this function and endExternalCommands() is not necessary within the |
| 5161 | \l{QSGRenderNode::render()}{render()} implementation of a QSGRenderNode |
| 5162 | because the scene graph performs the necessary steps implicitly for render |
| 5163 | nodes. |
| 5164 | |
| 5165 | Native graphics objects (such as, graphics device, command buffer or |
| 5166 | encoder) are accessible via QSGRendererInterface::getResource(). |
| 5167 | |
| 5168 | \warning Watch out for the fact that |
| 5169 | QSGRendererInterface::CommandListResource may return a different object |
| 5170 | between beginExternalCommands() - endExternalCommands(). This can happen |
| 5171 | when the underlying implementation provides a dedicated secondary command |
| 5172 | buffer for recording external graphics commands within a render pass. |
| 5173 | Therefore, always query CommandListResource after calling this function. Do |
| 5174 | not attempt to reuse an object from an earlier query. |
| 5175 | |
| 5176 | \note This function has no effect when the scene graph is using OpenGL |
| 5177 | directly and the RHI graphics abstraction layer is not in use. Refer to |
| 5178 | resetOpenGLState() in that case. |
| 5179 | |
| 5180 | \note When the scenegraph is using the RHI graphics abstraction layer with |
| 5181 | the OpenGL backend underneath, pay attention to the fact that the OpenGL |
| 5182 | state in the context can have arbitrary settings, and this function does not |
| 5183 | perform any resetting of the state back to defaults. Call |
| 5184 | resetOpenGLState() if that is seen necessary. |
| 5185 | |
| 5186 | \sa endExternalCommands(), resetOpenGLState() |
| 5187 | |
| 5188 | \since 5.14 |
| 5189 | */ |
| 5190 | void QQuickWindow::beginExternalCommands() |
| 5191 | { |
| 5192 | #if QT_CONFIG(opengl) /* || QT_CONFIG(vulkan) || defined(Q_OS_WIN) || defined(Q_OS_DARWIN) */ |
| 5193 | Q_D(QQuickWindow); |
| 5194 | if (d->rhi && d->context && d->context->isValid()) { |
| 5195 | QSGDefaultRenderContext *rc = static_cast<QSGDefaultRenderContext *>(d->context); |
| 5196 | QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer(); |
| 5197 | if (cb) |
| 5198 | cb->beginExternal(); |
| 5199 | } |
| 5200 | #endif |
| 5201 | } |
| 5202 | |
| 5203 | /*! |
| 5204 | When mixing raw graphics (OpenGL, Vulkan, Metal, etc.) commands with scene |
| 5205 | graph rendering, it is necessary to call this function after recording |
| 5206 | commands to the command buffer used by the scene graph to render its main |
| 5207 | render pass. This is to avoid clobbering state. |
| 5208 | |
| 5209 | In practice this function is often called from a slot connected to the |
| 5210 | beforeRenderPassRecording() or afterRenderPassRecording() signals. |
| 5211 | |
| 5212 | The function does not need to be called when recording commands to the |
| 5213 | application's own command buffer (such as, a VkCommandBuffer or |
| 5214 | MTLCommandBuffer + MTLRenderCommandEncoder created and managed by the |
| 5215 | application, not retrieved from the scene graph). With graphics APIs where |
| 5216 | no native command buffer concept is exposed (OpenGL, Direct 3D 11), |
| 5217 | beginExternalCommands() and endExternalCommands() together provide a |
| 5218 | replacement for resetOpenGLState(). |
| 5219 | |
| 5220 | Calling this function and beginExternalCommands() is not necessary within the |
| 5221 | \l{QSGRenderNode::render()}{render()} implementation of a QSGRenderNode |
| 5222 | because the scene graph performs the necessary steps implicitly for render |
| 5223 | nodes. |
| 5224 | |
| 5225 | \note This function has no effect when the scene graph is using OpenGL |
| 5226 | directly and the RHI graphics abstraction layer is not in use. Refer to |
| 5227 | resetOpenGLState() in that case. |
| 5228 | |
| 5229 | \sa beginExternalCommands() |
| 5230 | |
| 5231 | \since 5.14 |
| 5232 | */ |
| 5233 | void QQuickWindow::endExternalCommands() |
| 5234 | { |
| 5235 | #if QT_CONFIG(opengl) /* || QT_CONFIG(vulkan) || defined(Q_OS_WIN) || defined(Q_OS_DARWIN) */ |
| 5236 | Q_D(QQuickWindow); |
| 5237 | if (d->rhi && d->context && d->context->isValid()) { |
| 5238 | QSGDefaultRenderContext *rc = static_cast<QSGDefaultRenderContext *>(d->context); |
| 5239 | QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer(); |
| 5240 | if (cb) |
| 5241 | cb->endExternal(); |
| 5242 | } |
| 5243 | #endif |
| 5244 | } |
| 5245 | |
| 5246 | /*! |
| 5247 | \qmlproperty string Window::title |
| 5248 | |
| 5249 | The window's title in the windowing system. |
| 5250 | |
| 5251 | The window title might appear in the title area of the window decorations, |
| 5252 | depending on the windowing system and the window flags. It might also |
| 5253 | be used by the windowing system to identify the window in other contexts, |
| 5254 | such as in the task switcher. |
| 5255 | */ |
| 5256 | |
| 5257 | /*! |
| 5258 | \qmlproperty Qt::WindowModality Window::modality |
| 5259 | |
| 5260 | The modality of the window. |
| 5261 | |
| 5262 | A modal window prevents other windows from receiving input events. |
| 5263 | Possible values are Qt.NonModal (the default), Qt.WindowModal, |
| 5264 | and Qt.ApplicationModal. |
| 5265 | */ |
| 5266 | |
| 5267 | /*! |
| 5268 | \qmlproperty Qt::WindowFlags Window::flags |
| 5269 | |
| 5270 | The window flags of the window. |
| 5271 | |
| 5272 | The window flags control the window's appearance in the windowing system, |
| 5273 | whether it's a dialog, popup, or a regular window, and whether it should |
| 5274 | have a title bar, etc. |
| 5275 | |
| 5276 | The flags which you read from this property might differ from the ones |
| 5277 | that you set if the requested flags could not be fulfilled. |
| 5278 | |
| 5279 | \sa Qt::WindowFlags |
| 5280 | */ |
| 5281 | |
| 5282 | /*! |
| 5283 | \qmlattachedproperty Window Window::window |
| 5284 | \since 5.7 |
| 5285 | |
| 5286 | This attached property holds the item's window. |
| 5287 | The Window attached property can be attached to any Item. |
| 5288 | */ |
| 5289 | |
| 5290 | /*! |
| 5291 | \qmlattachedproperty int Window::width |
| 5292 | \qmlattachedproperty int Window::height |
| 5293 | \since 5.5 |
| 5294 | |
| 5295 | These attached properties hold the size of the item's window. |
| 5296 | The Window attached property can be attached to any Item. |
| 5297 | */ |
| 5298 | |
| 5299 | /*! |
| 5300 | \qmlproperty int Window::x |
| 5301 | \qmlproperty int Window::y |
| 5302 | \qmlproperty int Window::width |
| 5303 | \qmlproperty int Window::height |
| 5304 | |
| 5305 | Defines the window's position and size. |
| 5306 | |
| 5307 | The (x,y) position is relative to the \l Screen if there is only one, |
| 5308 | or to the virtual desktop (arrangement of multiple screens). |
| 5309 | |
| 5310 | \qml |
| 5311 | Window { x: 100; y: 100; width: 100; height: 100 } |
| 5312 | \endqml |
| 5313 | |
| 5314 | \image screen-and-window-dimensions.jpg |
| 5315 | */ |
| 5316 | |
| 5317 | /*! |
| 5318 | \qmlproperty int Window::minimumWidth |
| 5319 | \qmlproperty int Window::minimumHeight |
| 5320 | \since 5.1 |
| 5321 | |
| 5322 | Defines the window's minimum size. |
| 5323 | |
| 5324 | This is a hint to the window manager to prevent resizing below the specified |
| 5325 | width and height. |
| 5326 | */ |
| 5327 | |
| 5328 | /*! |
| 5329 | \qmlproperty int Window::maximumWidth |
| 5330 | \qmlproperty int Window::maximumHeight |
| 5331 | \since 5.1 |
| 5332 | |
| 5333 | Defines the window's maximum size. |
| 5334 | |
| 5335 | This is a hint to the window manager to prevent resizing above the specified |
| 5336 | width and height. |
| 5337 | */ |
| 5338 | |
| 5339 | /*! |
| 5340 | \qmlproperty bool Window::visible |
| 5341 | |
| 5342 | Whether the window is visible on the screen. |
| 5343 | |
| 5344 | Setting visible to false is the same as setting \l visibility to \l {QWindow::}{Hidden}. |
| 5345 | |
| 5346 | \sa visibility |
| 5347 | */ |
| 5348 | |
| 5349 | /*! |
| 5350 | \qmlproperty QWindow::Visibility Window::visibility |
| 5351 | |
| 5352 | The screen-occupation state of the window. |
| 5353 | |
| 5354 | Visibility is whether the window should appear in the windowing system as |
| 5355 | normal, minimized, maximized, fullscreen or hidden. |
| 5356 | |
| 5357 | To set the visibility to \l {QWindow::}{AutomaticVisibility} means to give the |
| 5358 | window a default visible state, which might be \l {QWindow::}{FullScreen} or |
| 5359 | \l {QWindow::}{Windowed} depending on the platform. However when reading the |
| 5360 | visibility property you will always get the actual state, never |
| 5361 | \c AutomaticVisibility. |
| 5362 | |
| 5363 | When a window is not visible its visibility is Hidden, and setting |
| 5364 | visibility to \l {QWindow::}{Hidden} is the same as setting \l visible to \c false. |
| 5365 | |
| 5366 | \sa visible |
| 5367 | \since 5.1 |
| 5368 | */ |
| 5369 | |
| 5370 | /*! |
| 5371 | \qmlattachedproperty QWindow::Visibility Window::visibility |
| 5372 | \since 5.4 |
| 5373 | |
| 5374 | This attached property holds whether the window is currently shown |
| 5375 | in the windowing system as normal, minimized, maximized, fullscreen or |
| 5376 | hidden. The \c Window attached property can be attached to any Item. If the |
| 5377 | item is not shown in any window, the value will be \l {QWindow::}{Hidden}. |
| 5378 | |
| 5379 | \sa visible, visibility |
| 5380 | */ |
| 5381 | |
| 5382 | /*! |
| 5383 | \qmlproperty Item Window::contentItem |
| 5384 | \readonly |
| 5385 | \brief The invisible root item of the scene. |
| 5386 | */ |
| 5387 | |
| 5388 | /*! |
| 5389 | \qmlproperty Qt::ScreenOrientation Window::contentOrientation |
| 5390 | |
| 5391 | This is a hint to the window manager in case it needs to display |
| 5392 | additional content like popups, dialogs, status bars, or similar |
| 5393 | in relation to the window. |
| 5394 | |
| 5395 | The recommended orientation is \l {Screen::orientation}{Screen.orientation}, but |
| 5396 | an application doesn't have to support all possible orientations, |
| 5397 | and thus can opt to ignore the current screen orientation. |
| 5398 | |
| 5399 | The difference between the window and the content orientation |
| 5400 | determines how much to rotate the content by. |
| 5401 | |
| 5402 | The default value is Qt::PrimaryOrientation. |
| 5403 | |
| 5404 | \sa Screen |
| 5405 | |
| 5406 | \since 5.1 |
| 5407 | */ |
| 5408 | |
| 5409 | /*! |
| 5410 | \qmlproperty real Window::opacity |
| 5411 | |
| 5412 | The opacity of the window. |
| 5413 | |
| 5414 | If the windowing system supports window opacity, this can be used to fade the |
| 5415 | window in and out, or to make it semitransparent. |
| 5416 | |
| 5417 | A value of 1.0 or above is treated as fully opaque, whereas a value of 0.0 or below |
| 5418 | is treated as fully transparent. Values inbetween represent varying levels of |
| 5419 | translucency between the two extremes. |
| 5420 | |
| 5421 | The default value is 1.0. |
| 5422 | |
| 5423 | \since 5.1 |
| 5424 | */ |
| 5425 | |
| 5426 | /*! |
| 5427 | \qmlproperty variant Window::screen |
| 5428 | |
| 5429 | The screen with which the window is associated. |
| 5430 | |
| 5431 | If specified before showing a window, will result in the window being shown |
| 5432 | on that screen, unless an explicit window position has been set. The value |
| 5433 | must be an element from the Qt.application.screens array. |
| 5434 | |
| 5435 | \note To ensure that the window is associated with the desired screen when |
| 5436 | the underlying native window is created, make sure this property is set as |
| 5437 | early as possible and that the setting of its value is not deferred. This |
| 5438 | can be particularly important on embedded platforms without a windowing system, |
| 5439 | where only one window per screen is allowed at a time. Setting the screen after |
| 5440 | a window has been created does not move the window if the new screen is part of |
| 5441 | the same virtual desktop as the old screen. |
| 5442 | |
| 5443 | \since 5.9 |
| 5444 | |
| 5445 | \sa QWindow::setScreen(), QWindow::screen(), QScreen, {QtQml::Qt::application}{Qt.application} |
| 5446 | */ |
| 5447 | |
| 5448 | /*! |
| 5449 | \qmlproperty QWindow Window::transientParent |
| 5450 | \since 5.13 |
| 5451 | |
| 5452 | The window for which this window is a transient pop-up. |
| 5453 | |
| 5454 | This is a hint to the window manager that this window is a dialog or pop-up |
| 5455 | on behalf of the transient parent. It usually means that the transient |
| 5456 | window will be centered over its transient parent when it is initially |
| 5457 | shown, that minimizing the parent window will also minimize the transient |
| 5458 | window, and so on; however results vary somewhat from platform to platform. |
| 5459 | |
| 5460 | Normally if you declare a Window inside an Item or inside another Window, |
| 5461 | this relationship is deduced automatically. In that case, if you declare |
| 5462 | this window's \l visible property \c true, it will not actually be shown |
| 5463 | until the \c transientParent window is shown. |
| 5464 | |
| 5465 | However if you set this property, then Qt Quick will no longer wait until |
| 5466 | the \c transientParent window is shown before showing this window. If you |
| 5467 | want to to be able to show a transient window independently of the "parent" |
| 5468 | Item or Window within which it was declared, you can remove that |
| 5469 | relationship by setting \c transientParent to \c null: |
| 5470 | |
| 5471 | \qml |
| 5472 | import QtQuick.Window 2.13 |
| 5473 | |
| 5474 | Window { |
| 5475 | // visible is false by default |
| 5476 | Window { |
| 5477 | transientParent: null |
| 5478 | visible: true |
| 5479 | } |
| 5480 | } |
| 5481 | \endqml |
| 5482 | |
| 5483 | In order to cause the window to be centered above its transient parent by |
| 5484 | default, depending on the window manager, it may also be necessary to set |
| 5485 | the \l Window::flags property with a suitable \l Qt::WindowType (such as |
| 5486 | \c Qt::Dialog). |
| 5487 | */ |
| 5488 | |
| 5489 | /*! |
| 5490 | \property QQuickWindow::transientParent |
| 5491 | \brief The window for which this window is a transient pop-up. |
| 5492 | \since 5.13 |
| 5493 | |
| 5494 | This is a hint to the window manager that this window is a dialog or pop-up |
| 5495 | on behalf of the transient parent, which may be any kind of \l QWindow. |
| 5496 | |
| 5497 | In order to cause the window to be centered above its transient parent by |
| 5498 | default, depending on the window manager, it may also be necessary to set |
| 5499 | the \l flags property with a suitable \l Qt::WindowType (such as \c Qt::Dialog). |
| 5500 | |
| 5501 | \sa parent() |
| 5502 | */ |
| 5503 | |
| 5504 | /*! |
| 5505 | \qmlproperty Item Window::activeFocusItem |
| 5506 | \since 5.1 |
| 5507 | |
| 5508 | The item which currently has active focus or \c null if there is |
| 5509 | no item with active focus. |
| 5510 | */ |
| 5511 | |
| 5512 | /*! |
| 5513 | \qmlattachedproperty Item Window::activeFocusItem |
| 5514 | \since 5.4 |
| 5515 | |
| 5516 | This attached property holds the item which currently has active focus or |
| 5517 | \c null if there is no item with active focus. The Window attached property |
| 5518 | can be attached to any Item. |
| 5519 | */ |
| 5520 | |
| 5521 | /*! |
| 5522 | \qmlproperty bool Window::active |
| 5523 | \since 5.1 |
| 5524 | |
| 5525 | The active status of the window. |
| 5526 | |
| 5527 | \sa requestActivate() |
| 5528 | */ |
| 5529 | |
| 5530 | /*! |
| 5531 | \qmlattachedproperty bool Window::active |
| 5532 | \since 5.4 |
| 5533 | |
| 5534 | This attached property tells whether the window is active. The Window |
| 5535 | attached property can be attached to any Item. |
| 5536 | |
| 5537 | Here is an example which changes a label to show the active state of the |
| 5538 | window in which it is shown: |
| 5539 | |
| 5540 | \qml |
| 5541 | import QtQuick 2.4 |
| 5542 | import QtQuick.Window 2.2 |
| 5543 | |
| 5544 | Text { |
| 5545 | text: Window.active ? "active" : "inactive" |
| 5546 | } |
| 5547 | \endqml |
| 5548 | */ |
| 5549 | |
| 5550 | /*! |
| 5551 | \qmlmethod QtQuick::Window::requestActivate() |
| 5552 | \since 5.1 |
| 5553 | |
| 5554 | Requests the window to be activated, i.e. receive keyboard focus. |
| 5555 | */ |
| 5556 | |
| 5557 | /*! |
| 5558 | \qmlmethod QtQuick::Window::alert(int msec) |
| 5559 | \since 5.1 |
| 5560 | |
| 5561 | Causes an alert to be shown for \a msec milliseconds. If \a msec is \c 0 |
| 5562 | (the default), then the alert is shown indefinitely until the window |
| 5563 | becomes active again. |
| 5564 | |
| 5565 | In alert state, the window indicates that it demands attention, for example |
| 5566 | by flashing or bouncing the taskbar entry. |
| 5567 | */ |
| 5568 | |
| 5569 | /*! |
| 5570 | \qmlmethod QtQuick::Window::close() |
| 5571 | |
| 5572 | Closes the window. |
| 5573 | |
| 5574 | When this method is called, or when the user tries to close the window by |
| 5575 | its title bar button, the \l closing signal will be emitted. If there is no |
| 5576 | handler, or the handler does not revoke permission to close, the window |
| 5577 | will subsequently close. If the QGuiApplication::quitOnLastWindowClosed |
| 5578 | property is \c true, and there are no other windows open, the application |
| 5579 | will quit. |
| 5580 | */ |
| 5581 | |
| 5582 | /*! |
| 5583 | \qmlmethod QtQuick::Window::raise() |
| 5584 | |
| 5585 | Raises the window in the windowing system. |
| 5586 | |
| 5587 | Requests that the window be raised to appear above other windows. |
| 5588 | */ |
| 5589 | |
| 5590 | /*! |
| 5591 | \qmlmethod QtQuick::Window::lower() |
| 5592 | |
| 5593 | Lowers the window in the windowing system. |
| 5594 | |
| 5595 | Requests that the window be lowered to appear below other windows. |
| 5596 | */ |
| 5597 | |
| 5598 | /*! |
| 5599 | \qmlmethod QtQuick::Window::show() |
| 5600 | |
| 5601 | Shows the window. |
| 5602 | |
| 5603 | This is equivalent to calling showFullScreen(), showMaximized(), or showNormal(), |
| 5604 | depending on the platform's default behavior for the window type and flags. |
| 5605 | |
| 5606 | \sa showFullScreen(), showMaximized(), showNormal(), hide(), QQuickItem::flags() |
| 5607 | */ |
| 5608 | |
| 5609 | /*! |
| 5610 | \qmlmethod QtQuick::Window::hide() |
| 5611 | |
| 5612 | Hides the window. |
| 5613 | |
| 5614 | Equivalent to setting \l visible to \c false or \l visibility to \l {QWindow::}{Hidden}. |
| 5615 | |
| 5616 | \sa show() |
| 5617 | */ |
| 5618 | |
| 5619 | /*! |
| 5620 | \qmlmethod QtQuick::Window::showMinimized() |
| 5621 | |
| 5622 | Shows the window as minimized. |
| 5623 | |
| 5624 | Equivalent to setting \l visibility to \l {QWindow::}{Minimized}. |
| 5625 | */ |
| 5626 | |
| 5627 | /*! |
| 5628 | \qmlmethod QtQuick::Window::showMaximized() |
| 5629 | |
| 5630 | Shows the window as maximized. |
| 5631 | |
| 5632 | Equivalent to setting \l visibility to \l {QWindow::}{Maximized}. |
| 5633 | */ |
| 5634 | |
| 5635 | /*! |
| 5636 | \qmlmethod QtQuick::Window::showFullScreen() |
| 5637 | |
| 5638 | Shows the window as fullscreen. |
| 5639 | |
| 5640 | Equivalent to setting \l visibility to \l {QWindow::}{FullScreen}. |
| 5641 | */ |
| 5642 | |
| 5643 | /*! |
| 5644 | \qmlmethod QtQuick::Window::showNormal() |
| 5645 | |
| 5646 | Shows the window as normal, i.e. neither maximized, minimized, nor fullscreen. |
| 5647 | |
| 5648 | Equivalent to setting \l visibility to \l {QWindow::}{Windowed}. |
| 5649 | */ |
| 5650 | |
| 5651 | /*! |
| 5652 | \enum QQuickWindow::RenderStage |
| 5653 | \since 5.4 |
| 5654 | |
| 5655 | \value BeforeSynchronizingStage Before synchronization. |
| 5656 | \value AfterSynchronizingStage After synchronization. |
| 5657 | \value BeforeRenderingStage Before rendering. |
| 5658 | \value AfterRenderingStage After rendering. |
| 5659 | \value AfterSwapStage After the frame is swapped. |
| 5660 | \value NoStage As soon as possible. This value was added in Qt 5.6. |
| 5661 | |
| 5662 | \sa {Scene Graph and Rendering} |
| 5663 | */ |
| 5664 | |
| 5665 | /*! |
| 5666 | \since 5.4 |
| 5667 | |
| 5668 | Schedules \a job to run when the rendering of this window reaches |
| 5669 | the given \a stage. |
| 5670 | |
| 5671 | This is a convenience to the equivalent signals in QQuickWindow for |
| 5672 | "one shot" tasks. |
| 5673 | |
| 5674 | The window takes ownership over \a job and will delete it when the |
| 5675 | job is completed. |
| 5676 | |
| 5677 | If rendering is shut down before \a job has a chance to run, the |
| 5678 | job will be run and then deleted as part of the scene graph cleanup. |
| 5679 | If the window is never shown and no rendering happens before the QQuickWindow |
| 5680 | is destroyed, all pending jobs will be destroyed without their run() |
| 5681 | method being called. |
| 5682 | |
| 5683 | If the rendering is happening on a different thread, then the job |
| 5684 | will happen on the rendering thread. |
| 5685 | |
| 5686 | If \a stage is \l NoStage, \a job will be run at the earliest opportunity |
| 5687 | whenever the render thread is not busy rendering a frame. If there is no |
| 5688 | OpenGL context available or the window is not exposed at the time the job is |
| 5689 | either posted or handled, it is deleted without executing the run() method. |
| 5690 | If a non-threaded renderer is in use, the run() method of the job is executed |
| 5691 | synchronously. |
| 5692 | The OpenGL context is changed to the renderer context before executing a |
| 5693 | \l NoStage job. |
| 5694 | |
| 5695 | \note This function does not trigger rendering; the jobs targeting any other |
| 5696 | stage than NoStage will be stored run until rendering is triggered elsewhere. |
| 5697 | To force the job to run earlier, call QQuickWindow::update(); |
| 5698 | |
| 5699 | \sa beforeRendering(), afterRendering(), beforeSynchronizing(), |
| 5700 | afterSynchronizing(), frameSwapped(), sceneGraphInvalidated() |
| 5701 | */ |
| 5702 | |
| 5703 | void QQuickWindow::scheduleRenderJob(QRunnable *job, RenderStage stage) |
| 5704 | { |
| 5705 | Q_D(QQuickWindow); |
| 5706 | |
| 5707 | d->renderJobMutex.lock(); |
| 5708 | if (stage == BeforeSynchronizingStage) { |
| 5709 | d->beforeSynchronizingJobs << job; |
| 5710 | } else if (stage == AfterSynchronizingStage) { |
| 5711 | d->afterSynchronizingJobs << job; |
| 5712 | } else if (stage == BeforeRenderingStage) { |
| 5713 | d->beforeRenderingJobs << job; |
| 5714 | } else if (stage == AfterRenderingStage) { |
| 5715 | d->afterRenderingJobs << job; |
| 5716 | } else if (stage == AfterSwapStage) { |
| 5717 | d->afterSwapJobs << job; |
| 5718 | } else if (stage == NoStage) { |
| 5719 | if (d->renderControl && openglContext() |
| 5720 | #if QT_CONFIG(opengl) |
| 5721 | && openglContext()->thread() == QThread::currentThread() |
| 5722 | #endif |
| 5723 | ) { |
| 5724 | job->run(); |
| 5725 | delete job; |
| 5726 | } else if (isExposed()) { |
| 5727 | d->windowManager->postJob(window: this, job); |
| 5728 | } else { |
| 5729 | delete job; |
| 5730 | } |
| 5731 | } |
| 5732 | d->renderJobMutex.unlock(); |
| 5733 | } |
| 5734 | |
| 5735 | void QQuickWindowPrivate::runAndClearJobs(QList<QRunnable *> *jobs) |
| 5736 | { |
| 5737 | renderJobMutex.lock(); |
| 5738 | QList<QRunnable *> jobList = *jobs; |
| 5739 | jobs->clear(); |
| 5740 | renderJobMutex.unlock(); |
| 5741 | |
| 5742 | for (QRunnable *r : qAsConst(t&: jobList)) { |
| 5743 | r->run(); |
| 5744 | delete r; |
| 5745 | } |
| 5746 | } |
| 5747 | |
| 5748 | void QQuickWindow::runJobsAfterSwap() |
| 5749 | { |
| 5750 | Q_D(QQuickWindow); |
| 5751 | d->runAndClearJobs(jobs: &d->afterSwapJobs); |
| 5752 | } |
| 5753 | |
| 5754 | /*! |
| 5755 | * Returns the device pixel ratio for this window. |
| 5756 | * |
| 5757 | * This is different from QWindow::devicePixelRatio() in that it supports |
| 5758 | * redirected rendering via QQuickRenderControl. When using a |
| 5759 | * QQuickRenderControl, the QQuickWindow is often not created, meaning it is |
| 5760 | * never shown and there is no underlying native window created in the |
| 5761 | * windowing system. As a result, querying properties like the device pixel |
| 5762 | * ratio cannot give correct results. Use this function instead. |
| 5763 | * |
| 5764 | * \sa QWindow::devicePixelRatio() |
| 5765 | */ |
| 5766 | qreal QQuickWindow::effectiveDevicePixelRatio() const |
| 5767 | { |
| 5768 | QWindow *w = QQuickRenderControl::renderWindowFor(win: const_cast<QQuickWindow *>(this)); |
| 5769 | return w ? w->devicePixelRatio() : devicePixelRatio(); |
| 5770 | } |
| 5771 | |
| 5772 | /*! |
| 5773 | \return the current renderer interface. The value is always valid and is never null. |
| 5774 | |
| 5775 | \note This function can be called at any time after constructing the |
| 5776 | QQuickWindow, even while isSceneGraphInitialized() is still false. However, |
| 5777 | some renderer interface functions, in particular |
| 5778 | QSGRendererInterface::getResource() will not be functional until the |
| 5779 | scenegraph is up and running. Backend queries, like |
| 5780 | QSGRendererInterface::graphicsApi() or QSGRendererInterface::shaderType(), |
| 5781 | will always be functional on the other hand. |
| 5782 | |
| 5783 | \note The ownership of the returned pointer stays with Qt. The returned |
| 5784 | instance may or may not be shared between different QQuickWindow instances, |
| 5785 | depending on the scenegraph backend in use. Therefore applications are |
| 5786 | expected to query the interface object for each QQuickWindow instead of |
| 5787 | reusing the already queried pointer. |
| 5788 | |
| 5789 | \sa QSGRenderNode, QSGRendererInterface |
| 5790 | |
| 5791 | \since 5.8 |
| 5792 | */ |
| 5793 | QSGRendererInterface *QQuickWindow::rendererInterface() const |
| 5794 | { |
| 5795 | Q_D(const QQuickWindow); |
| 5796 | |
| 5797 | // no context validity check - it is essential to be able to return a |
| 5798 | // renderer interface instance before scenegraphInitialized() is emitted |
| 5799 | // (depending on the backend, that can happen way too late for some of the |
| 5800 | // rif use cases, like examining the graphics api or shading language in |
| 5801 | // use) |
| 5802 | |
| 5803 | return d->context->sceneGraphContext()->rendererInterface(renderContext: d->context); |
| 5804 | } |
| 5805 | |
| 5806 | /*! |
| 5807 | Requests a Qt Quick scenegraph backend for the specified graphics \a api. |
| 5808 | Backends can either be built-in or be installed in form of dynamically |
| 5809 | loaded plugins. |
| 5810 | |
| 5811 | \note The call to the function must happen before constructing the first |
| 5812 | QQuickWindow in the application. It cannot be changed afterwards. |
| 5813 | |
| 5814 | If the selected backend is invalid or an error occurs, the default backend |
| 5815 | (OpenGL or software, depending on the Qt configuration) is used. |
| 5816 | |
| 5817 | \since 5.8 |
| 5818 | */ |
| 5819 | void QQuickWindow::setSceneGraphBackend(QSGRendererInterface::GraphicsApi api) |
| 5820 | { |
| 5821 | switch (api) { |
| 5822 | case QSGRendererInterface::Software: |
| 5823 | setSceneGraphBackend(QStringLiteral("software" )); |
| 5824 | break; |
| 5825 | case QSGRendererInterface::Direct3D12: |
| 5826 | setSceneGraphBackend(QStringLiteral("d3d12" )); |
| 5827 | break; |
| 5828 | default: |
| 5829 | break; |
| 5830 | } |
| 5831 | #if QT_CONFIG(opengl) /* || QT_CONFIG(vulkan) || defined(Q_OS_WIN) || defined(Q_OS_DARWIN) */ |
| 5832 | if (QSGRendererInterface::isApiRhiBased(api)) |
| 5833 | QSGRhiSupport::configure(api); |
| 5834 | #endif |
| 5835 | } |
| 5836 | |
| 5837 | /*! |
| 5838 | Requests the specified Qt Quick scenegraph \a backend. Backends can either |
| 5839 | be built-in or be installed in form of dynamically loaded plugins. |
| 5840 | |
| 5841 | \overload |
| 5842 | |
| 5843 | \note The call to the function must happen before constructing the first |
| 5844 | QQuickWindow in the application. It cannot be changed afterwards. |
| 5845 | |
| 5846 | If \a backend is invalid or an error occurs, the default backend (OpenGL or |
| 5847 | software, depending on the Qt configuration) is used. |
| 5848 | |
| 5849 | \note Calling this function is equivalent to setting the |
| 5850 | \c QT_QUICK_BACKEND or \c QMLSCENE_DEVICE environment variables. However, this |
| 5851 | API is safer to use in applications that spawn other processes as there is |
| 5852 | no need to worry about environment inheritance. |
| 5853 | |
| 5854 | \since 5.8 |
| 5855 | */ |
| 5856 | void QQuickWindow::setSceneGraphBackend(const QString &backend) |
| 5857 | { |
| 5858 | QSGContext::setBackend(backend); |
| 5859 | } |
| 5860 | |
| 5861 | /*! |
| 5862 | Returns the requested Qt Quick scenegraph backend. |
| 5863 | |
| 5864 | \note The return value of this function may still be outdated by |
| 5865 | subsequent calls to setSceneGraphBackend() until the first QQuickWindow in the |
| 5866 | application has been constructed. |
| 5867 | |
| 5868 | \since 5.9 |
| 5869 | */ |
| 5870 | QString QQuickWindow::sceneGraphBackend() |
| 5871 | { |
| 5872 | return QSGContext::backend(); |
| 5873 | } |
| 5874 | |
| 5875 | /*! |
| 5876 | Creates a simple rectangle node. When the scenegraph is not initialized, the return value is null. |
| 5877 | |
| 5878 | This is cross-backend alternative to constructing a QSGSimpleRectNode directly. |
| 5879 | |
| 5880 | \since 5.8 |
| 5881 | \sa QSGRectangleNode |
| 5882 | */ |
| 5883 | QSGRectangleNode *QQuickWindow::createRectangleNode() const |
| 5884 | { |
| 5885 | Q_D(const QQuickWindow); |
| 5886 | return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createRectangleNode() : nullptr; |
| 5887 | } |
| 5888 | |
| 5889 | /*! |
| 5890 | Creates a simple image node. When the scenegraph is not initialized, the return value is null. |
| 5891 | |
| 5892 | This is cross-backend alternative to constructing a QSGSimpleTextureNode directly. |
| 5893 | |
| 5894 | \since 5.8 |
| 5895 | \sa QSGImageNode |
| 5896 | */ |
| 5897 | QSGImageNode *QQuickWindow::createImageNode() const |
| 5898 | { |
| 5899 | Q_D(const QQuickWindow); |
| 5900 | return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createImageNode() : nullptr; |
| 5901 | } |
| 5902 | |
| 5903 | /*! |
| 5904 | Creates a nine patch node. When the scenegraph is not initialized, the return value is null. |
| 5905 | |
| 5906 | \since 5.8 |
| 5907 | */ |
| 5908 | QSGNinePatchNode *QQuickWindow::createNinePatchNode() const |
| 5909 | { |
| 5910 | Q_D(const QQuickWindow); |
| 5911 | return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createNinePatchNode() : nullptr; |
| 5912 | } |
| 5913 | |
| 5914 | /*! |
| 5915 | \since 5.10 |
| 5916 | |
| 5917 | Returns the render type of text-like elements in Qt Quick. |
| 5918 | The default is QQuickWindow::QtTextRendering. |
| 5919 | |
| 5920 | \sa setTextRenderType() |
| 5921 | */ |
| 5922 | QQuickWindow::TextRenderType QQuickWindow::textRenderType() |
| 5923 | { |
| 5924 | return QQuickWindowPrivate::textRenderType; |
| 5925 | } |
| 5926 | |
| 5927 | /*! |
| 5928 | \since 5.10 |
| 5929 | |
| 5930 | Sets the default render type of text-like elements in Qt Quick to \a renderType. |
| 5931 | |
| 5932 | \note setting the render type will only affect elements created afterwards; |
| 5933 | the render type of existing elements will not be modified. |
| 5934 | |
| 5935 | \sa textRenderType() |
| 5936 | */ |
| 5937 | void QQuickWindow::setTextRenderType(QQuickWindow::TextRenderType renderType) |
| 5938 | { |
| 5939 | QQuickWindowPrivate::textRenderType = renderType; |
| 5940 | } |
| 5941 | |
| 5942 | #ifndef QT_NO_DEBUG_STREAM |
| 5943 | QDebug operator<<(QDebug debug, const QQuickWindow *win) |
| 5944 | { |
| 5945 | QDebugStateSaver saver(debug); |
| 5946 | debug.nospace(); |
| 5947 | if (!win) { |
| 5948 | debug << "QQuickWindow(0)" ; |
| 5949 | return debug; |
| 5950 | } |
| 5951 | |
| 5952 | debug << win->metaObject()->className() << '(' << static_cast<const void *>(win); |
| 5953 | if (win->isActive()) |
| 5954 | debug << " active" ; |
| 5955 | if (win->isExposed()) |
| 5956 | debug << " exposed" ; |
| 5957 | debug << ", visibility=" << win->visibility() << ", flags=" << win->flags(); |
| 5958 | if (!win->title().isEmpty()) |
| 5959 | debug << ", title=" << win->title(); |
| 5960 | if (!win->objectName().isEmpty()) |
| 5961 | debug << ", name=" << win->objectName(); |
| 5962 | if (win->parent()) |
| 5963 | debug << ", parent=" << static_cast<const void *>(win->parent()); |
| 5964 | if (win->transientParent()) |
| 5965 | debug << ", transientParent=" << static_cast<const void *>(win->transientParent()); |
| 5966 | debug << ", geometry=" ; |
| 5967 | QtDebugUtils::formatQRect(debug, rect: win->geometry()); |
| 5968 | debug << ')'; |
| 5969 | return debug; |
| 5970 | } |
| 5971 | #endif |
| 5972 | |
| 5973 | #include "moc_qquickwindow.cpp" |
| 5974 | |
| 5975 | QT_END_NAMESPACE |
| 5976 | |