/
qquickcanvas.cpp
2384 lines (1951 loc) · 77.1 KB
1
2
/****************************************************************************
**
3
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/
5
**
6
** This file is part of the QtQml module of the Qt Toolkit.
7
8
9
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
10
11
12
13
14
15
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16
17
**
** In addition, as a special exception, Nokia gives you certain additional
18
** rights. These rights are described in the Nokia Qt LGPL Exception
19
20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
21
22
23
24
25
26
27
28
29
30
31
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
32
33
34
35
36
**
**
**
**
**
37
**
38
39
40
41
** $QT_END_LICENSE$
**
****************************************************************************/
42
43
#include "qquickcanvas.h"
#include "qquickcanvas_p.h"
44
45
46
#include "qquickitem.h"
#include "qquickitem_p.h"
47
#include "qquickevents_p_p.h"
48
49
50
51
52
#include <QtQuick/private/qsgrenderer_p.h>
#include <QtQuick/private/qsgtexture_p.h>
#include <QtQuick/private/qsgflashnode_p.h>
#include <QtQuick/qsgengine.h>
53
54
#include <private/qquickwindowmanager_p.h>
55
56
#include <private/qguiapplication_p.h>
57
#include <QtGui/QInputMethod>
58
#include <QtGui/QCursor>
59
60
61
#include <private/qabstractanimation_p.h>
62
#include <QtGui/qpainter.h>
63
#include <QtGui/qevent.h>
64
#include <QtGui/qmatrix4x4.h>
65
#include <QtGui/qstylehints.h>
66
67
#include <QtCore/qvarlengtharray.h>
#include <QtCore/qabstractanimation.h>
68
#include <QtQml/qqmlincubator.h>
69
70
#include <QtQuick/private/qquickpixmapcache_p.h>
71
72
#include <private/qqmlprofilerservice_p.h>
73
74
75
QT_BEGIN_NAMESPACE
76
DEFINE_BOOL_CONFIG_OPTION(qmlTranslateTouchToMouse, QML_TRANSLATE_TOUCH_TO_MOUSE)
77
78
void QQuickCanvasPrivate::updateFocusItemTransform()
79
{
80
81
Q_Q(QQuickCanvas);
QQuickItem *focus = q->activeFocusItem();
82
if (focus && qApp->focusObject() == focus)
83
qApp->inputMethod()->setInputItemTransform(QQuickItemPrivate::get(focus)->itemToCanvasTransform());
84
85
}
86
class QQuickCanvasIncubationController : public QObject, public QQmlIncubationController
87
88
{
public:
89
QQuickCanvasIncubationController(QQuickCanvasPrivate *canvas)
90
91
92
93
94
95
96
: m_canvas(canvas), m_eventSent(false) {}
protected:
virtual bool event(QEvent *e)
{
if (e->type() == QEvent::User) {
Q_ASSERT(m_eventSent);
97
volatile bool *amtp = m_canvas->windowManager->allowMainThreadProcessing();
98
99
while (incubatingObjectCount()) {
if (amtp)
100
incubateWhile(amtp, 2);
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
else
incubateFor(5);
QCoreApplication::processEvents();
}
m_eventSent = false;
}
return QObject::event(e);
}
virtual void incubatingObjectCountChanged(int count)
{
if (count && !m_eventSent) {
m_eventSent = true;
QCoreApplication::postEvent(this, new QEvent(QEvent::User));
}
117
118
// If no animations are running, the renderer may be waiting
m_canvas->windowManager->wakeup();
119
120
121
}
private:
122
QQuickCanvasPrivate *m_canvas;
123
124
125
bool m_eventSent;
};
126
127
128
129
QAccessibleInterface *QQuickCanvas::accessibleRoot() const
{
return QAccessible::queryAccessibleInterface(const_cast<QQuickCanvas*>(this));
}
130
131
132
133
134
135
/*
Focus behavior
==============
136
Prior to being added to a valid canvas items can set and clear focus with no
137
effect. Only once items are added to a canvas (by way of having a parent set that
138
already belongs to a canvas) do the focus rules apply. Focus goes back to
139
140
141
having no effect if an item is removed from a canvas.
When an item is moved into a new focus scope (either being added to a canvas
142
for the first time, or having its parent changed), if the focus scope already has
143
a scope focused item that takes precedence over the item being added. Otherwise,
144
the focus of the added tree is used. In the case of of a tree of items being
145
added to a canvas for the first time, which may have a conflicted focus state (two
146
or more items in one scope having focus set), the same rule is applied item by item -
147
thus the first item that has focus will get it (assuming the scope doesn't already
148
149
150
have a scope focused item), and the other items will have their focus cleared.
*/
151
152
153
154
155
156
// #define FOCUS_DEBUG
// #define MOUSE_DEBUG
// #define TOUCH_DEBUG
// #define DIRTY_DEBUG
157
QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData()
158
159
160
161
: transformNode(0)
{
}
162
QQuickRootItem::QQuickRootItem()
163
164
165
{
}
166
/*! \reimp */
167
void QQuickCanvas::exposeEvent(QExposeEvent *)
168
{
169
Q_D(QQuickCanvas);
170
d->windowManager->exposureChanged(this);
171
172
}
173
/*! \reimp */
174
void QQuickCanvas::resizeEvent(QResizeEvent *)
175
{
176
Q_D(QQuickCanvas);
177
d->windowManager->resize(this, size());
178
179
}
180
/*! \reimp */
181
void QQuickCanvas::showEvent(QShowEvent *)
182
{
183
d_func()->windowManager->show(this);
184
185
}
186
/*! \reimp */
187
void QQuickCanvas::hideEvent(QHideEvent *)
188
{
189
d_func()->windowManager->hide(this);
190
191
}
192
/*! \reimp */
193
194
195
196
197
198
void QQuickCanvas::focusOutEvent(QFocusEvent *)
{
Q_D(QQuickCanvas);
d->rootItem->setFocus(false);
}
199
/*! \reimp */
200
201
202
203
void QQuickCanvas::focusInEvent(QFocusEvent *)
{
Q_D(QQuickCanvas);
d->rootItem->setFocus(true);
204
d->updateFocusItemTransform();
205
}
206
207
208
void QQuickCanvasPrivate::polishItems()
209
{
210
211
212
213
214
QSet<QQuickItem *> itms = itemsToPolish;
itemsToPolish.clear();
for (QSet<QQuickItem *>::iterator it = itms.begin(); it != itms.end(); ++it) {
QQuickItem *item = *it;
215
QQuickItemPrivate::get(item)->polishScheduled = false;
216
217
item->updatePolish();
}
218
updateFocusItemTransform();
219
220
}
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
/**
* This parameter enables that this canvas can be rendered without
* being shown on screen. This feature is very limited in what it supports.
*
* There needs to be another window actually showing that we can make current
* to get a surface to make current AND for this feature to be useful
* one needs to hook into beforeRender() and set the render tareget.
*
*/
void QQuickCanvasPrivate::setRenderWithoutShowing(bool render)
{
if (render == renderWithoutShowing)
return;
Q_Q(QQuickCanvas);
renderWithoutShowing = render;
if (render)
windowManager->show(q);
else
windowManager->hide(q);
}
245
246
247
248
249
250
251
252
253
254
void forceUpdate(QQuickItem *item)
{
if (item->flags() & QQuickItem::ItemHasContents)
item->update();
QQuickItemPrivate::get(item)->dirty(QQuickItemPrivate::ChildrenUpdateMask);
QList <QQuickItem *> items = item->childItems();
for (int i=0; i<items.size(); ++i)
forceUpdate(items.at(i));
}
255
256
void QQuickCanvasPrivate::syncSceneGraph()
257
{
258
if (!renderer) {
259
260
forceUpdate(rootItem);
261
262
263
264
265
266
QSGRootNode *rootNode = new QSGRootNode;
rootNode->appendChildNode(QQuickItemPrivate::get(rootItem)->itemNode());
renderer = context->createRenderer();
renderer->setRootNode(rootNode);
}
267
updateDirtyNodes();
268
269
// Copy the current state of clearing from canvas into renderer.
270
renderer->setClearColor(clearColor);
271
272
273
QSGRenderer::ClearMode mode = QSGRenderer::ClearStencilBuffer | QSGRenderer::ClearDepthBuffer;
if (clearBeforeRendering)
mode |= QSGRenderer::ClearColorBuffer;
274
renderer->setClearMode(mode);
275
276
277
}
278
void QQuickCanvasPrivate::renderSceneGraph(const QSize &size)
279
{
280
Q_Q(QQuickCanvas);
281
282
emit q->beforeRendering();
int fboId = 0;
283
renderer->setDeviceRect(QRect(QPoint(0, 0), size));
284
285
286
287
288
289
if (renderTargetId) {
fboId = renderTargetId;
renderer->setViewportRect(QRect(QPoint(0, 0), renderTargetSize));
} else {
renderer->setViewportRect(QRect(QPoint(0, 0), size));
}
290
renderer->setProjectionMatrixToDeviceRect();
291
292
context->renderNextFrame(renderer, fboId);
293
emit q->afterRendering();
294
295
}
296
QQuickCanvasPrivate::QQuickCanvasPrivate()
297
298
299
: rootItem(0)
, activeFocusItem(0)
, mouseGrabberItem(0)
300
301
, touchMouseId(-1)
, touchMousePressTimestamp(0)
302
, renderWithoutShowing(false)
303
304
, dirtyItemList(0)
, context(0)
305
306
, renderer(0)
, windowManager(0)
307
308
, clearColor(Qt::white)
, clearBeforeRendering(true)
309
310
, persistentGLContext(false)
, persistentSceneGraph(false)
311
, renderTarget(0)
312
, renderTargetId(0)
313
, incubationController(0)
314
315
316
{
}
317
QQuickCanvasPrivate::~QQuickCanvasPrivate()
318
319
320
{
}
321
void QQuickCanvasPrivate::init(QQuickCanvas *c)
322
323
324
{
q_ptr = c;
325
Q_Q(QQuickCanvas);
326
327
328
rootItem = new QQuickRootItem;
QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(rootItem);
329
rootItemPrivate->canvas = q;
330
rootItemPrivate->flags |= QQuickItem::ItemIsFocusScope;
331
332
333
334
335
336
337
// In the absence of a focus in event on some platforms assume the window will
// be activated immediately and set focus on the rootItem
// ### Remove when QTBUG-22415 is resolved.
//It is important that this call happens after the rootItem has a canvas..
rootItem->setFocus(true);
338
339
windowManager = QQuickWindowManager::instance();
context = windowManager->sceneGraphContext();
340
q->setSurfaceType(QWindow::OpenGLSurface);
341
q->setFormat(context->defaultSurfaceFormat());
342
343
344
345
QObject::connect(context, SIGNAL(initialized()), q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection);
QObject::connect(context, SIGNAL(invalidated()), q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection);
QObject::connect(context, SIGNAL(invalidated()), q, SLOT(cleanupSceneGraph()), Qt::DirectConnection);
346
347
348
349
// ### TODO: remove QSGEngine
engine = new QSGEngine();
engine->setCanvas(q);
350
351
}
352
QQmlListProperty<QObject> QQuickCanvasPrivate::data()
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
{
initRootItem();
return QQuickItemPrivate::get(rootItem)->data();
}
void QQuickCanvasPrivate::initRootItem()
{
Q_Q(QQuickCanvas);
q->connect(q, SIGNAL(widthChanged(int)),
rootItem, SLOT(setWidth(int)));
q->connect(q, SIGNAL(heightChanged(int)),
rootItem, SLOT(setHeight(int)));
rootItem->setWidth(q->width());
rootItem->setHeight(q->height());
}
369
370
371
372
373
374
375
376
377
378
static QQuickMouseEventEx touchToMouseEvent(QEvent::Type type, const QTouchEvent::TouchPoint &p)
{
QQuickMouseEventEx me(type, p.pos(), p.scenePos(), p.screenPos(),
Qt::LeftButton, Qt::LeftButton, 0);
me.setVelocity(p.velocity());
return me;
}
void QQuickCanvasPrivate::translateTouchToMouse(QTouchEvent *event)
{
379
380
if (event->type() == QEvent::TouchCancel) {
touchMouseId = -1;
381
382
if (mouseGrabberItem)
mouseGrabberItem->ungrabMouse();
383
384
return;
}
385
386
387
388
389
390
391
392
393
394
for (int i = 0; i < event->touchPoints().count(); ++i) {
QTouchEvent::TouchPoint p = event->touchPoints().at(i);
if (touchMouseId == -1 && p.state() & Qt::TouchPointPressed) {
bool doubleClick = event->timestamp() - touchMousePressTimestamp
< static_cast<ulong>(qApp->styleHints()->mouseDoubleClickInterval());
touchMousePressTimestamp = event->timestamp();
if (doubleClick) {
QQuickMouseEventEx me = touchToMouseEvent(QEvent::MouseButtonDblClick, p);
me.setTimestamp(event->timestamp());
me.setAccepted(false);
395
me.setCapabilities(event->device()->capabilities());
396
397
398
399
400
401
402
403
404
405
406
if (!mouseGrabberItem) {
if (deliverInitialMousePressEvent(rootItem, &me)) {
touchMouseId = p.id();
event->setAccepted(true);
}
} else {
deliverMouseEvent(&me);
if (me.isAccepted()) {
touchMouseId = p.id();
event->setAccepted(true);
}
407
408
409
410
411
}
}
QQuickMouseEventEx me = touchToMouseEvent(QEvent::MouseButtonPress, p);
me.setTimestamp(event->timestamp());
me.setAccepted(false);
412
me.setCapabilities(event->device()->capabilities());
413
414
415
416
417
deliverMouseEvent(&me);
if (me.isAccepted()) {
touchMouseId = p.id();
event->setAccepted(true);
}
418
419
if (touchMouseId != -1)
break;
420
421
422
423
} else if (p.id() == touchMouseId) {
if (p.state() & Qt::TouchPointMoved) {
QQuickMouseEventEx me = touchToMouseEvent(QEvent::MouseMove, p);
me.setTimestamp(event->timestamp());
424
me.setCapabilities(event->device()->capabilities());
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
if (!mouseGrabberItem) {
if (lastMousePosition.isNull())
lastMousePosition = me.windowPos();
QPointF last = lastMousePosition;
lastMousePosition = me.windowPos();
bool accepted = me.isAccepted();
bool delivered = deliverHoverEvent(rootItem, me.windowPos(), last, me.modifiers(), accepted);
if (!delivered) {
//take care of any exits
accepted = clearHover();
}
me.setAccepted(accepted);
break;
}
deliverMouseEvent(&me);
} else if (p.state() & Qt::TouchPointReleased) {
touchMouseId = -1;
if (!mouseGrabberItem)
return;
QQuickMouseEventEx me = touchToMouseEvent(QEvent::MouseButtonRelease, p);
me.setTimestamp(event->timestamp());
448
me.setCapabilities(event->device()->capabilities());
449
deliverMouseEvent(&me);
450
451
if (mouseGrabberItem)
mouseGrabberItem->ungrabMouse();
452
453
454
455
456
457
}
break;
}
}
}
458
void QQuickCanvasPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
459
460
461
462
463
464
465
466
467
468
469
470
471
472
{
for (int i=0; i<touchPoints.count(); i++) {
QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
touchPoint.setRect(transform.mapRect(touchPoint.sceneRect()));
touchPoint.setStartPos(transform.map(touchPoint.startScenePos()));
touchPoint.setLastPos(transform.map(touchPoint.lastScenePos()));
}
}
/*!
Translates the data in \a touchEvent to this canvas. This method leaves the item local positions in
\a touchEvent untouched (these are filled in later).
*/
473
void QQuickCanvasPrivate::translateTouchEvent(QTouchEvent *touchEvent)
474
{
475
// Q_Q(QQuickCanvas);
476
477
// touchEvent->setWidget(q); // ### refactor...
478
479
480
481
482
483
484
485
486
487
488
489
490
QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
for (int i = 0; i < touchPoints.count(); ++i) {
QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
touchPoint.setScreenRect(touchPoint.sceneRect());
touchPoint.setStartScreenPos(touchPoint.startScenePos());
touchPoint.setLastScreenPos(touchPoint.lastScenePos());
touchPoint.setSceneRect(touchPoint.rect());
touchPoint.setStartScenePos(touchPoint.startPos());
touchPoint.setLastScenePos(touchPoint.lastPos());
491
if (i == 0)
492
493
494
495
496
lastMousePosition = touchPoint.pos().toPoint();
}
touchEvent->setTouchPoints(touchPoints);
}
497
void QQuickCanvasPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
498
{
499
Q_Q(QQuickCanvas);
500
501
Q_ASSERT(item);
502
Q_ASSERT(scope || item == rootItem);
503
504
#ifdef FOCUS_DEBUG
505
qWarning() << "QQuickCanvasPrivate::setFocusInScope():";
506
qWarning() << " scope:" << (QObject *)scope;
507
if (scope)
508
qWarning() << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(scope)->subFocusItem;
509
510
511
512
qWarning() << " item:" << (QObject *)item;
qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
#endif
513
514
QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0;
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
515
516
517
QQuickItem *oldActiveFocusItem = 0;
QQuickItem *newActiveFocusItem = 0;
518
519
QVarLengthArray<QQuickItem *, 20> changed;
520
521
// Does this change the active focus?
522
if (item == rootItem || (scopePrivate->activeFocus && item->isEnabled())) {
523
524
oldActiveFocusItem = activeFocusItem;
newActiveFocusItem = item;
525
526
527
while (newActiveFocusItem->isFocusScope()
&& newActiveFocusItem->scopedFocusItem()
&& newActiveFocusItem->scopedFocusItem()->isEnabled()) {
528
newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
529
}
530
531
if (oldActiveFocusItem) {
532
#ifndef QT_NO_IM
533
qApp->inputMethod()->reset();
534
535
#endif
536
537
538
539
activeFocusItem = 0;
QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
q->sendEvent(oldActiveFocusItem, &event);
540
QQuickItem *afi = oldActiveFocusItem;
541
while (afi != scope) {
542
543
if (QQuickItemPrivate::get(afi)->activeFocus) {
QQuickItemPrivate::get(afi)->activeFocus = false;
544
545
546
changed << afi;
}
afi = afi->parentItem();
547
548
549
550
}
}
}
551
if (item != rootItem && !(options & DontChangeSubFocusItem)) {
552
QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
553
if (oldSubFocusItem) {
554
QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
555
556
changed << oldSubFocusItem;
}
557
558
QQuickItemPrivate::get(item)->updateSubFocusItem(scope, true);
559
560
561
}
if (!(options & DontChangeFocusProperty)) {
562
// if (item != rootItem || QGuiApplication::focusWindow() == q) { // QTBUG-22415
563
564
itemPrivate->focus = true;
changed << item;
565
// }
566
567
}
568
if (newActiveFocusItem && rootItem->hasFocus()) {
569
570
activeFocusItem = newActiveFocusItem;
571
QQuickItemPrivate::get(newActiveFocusItem)->activeFocus = true;
572
573
changed << newActiveFocusItem;
574
QQuickItem *afi = newActiveFocusItem->parentItem();
575
while (afi && afi != scope) {
576
if (afi->isFocusScope()) {
577
QQuickItemPrivate::get(afi)->activeFocus = true;
578
579
580
581
582
583
changed << afi;
}
afi = afi->parentItem();
}
QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
584
q->sendEvent(newActiveFocusItem, &event);
585
586
}
587
588
emit q->focusObjectChanged(activeFocusItem);
589
if (!changed.isEmpty())
590
591
592
notifyFocusChangesRecur(changed.data(), changed.count() - 1);
}
593
void QQuickCanvasPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
594
{
595
Q_Q(QQuickCanvas);
596
597
Q_ASSERT(item);
598
Q_ASSERT(scope || item == rootItem);
599
600
#ifdef FOCUS_DEBUG
601
qWarning() << "QQuickCanvasPrivate::clearFocusInScope():";
602
603
604
605
606
qWarning() << " scope:" << (QObject *)scope;
qWarning() << " item:" << (QObject *)item;
qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
#endif
607
608
609
610
611
612
QQuickItemPrivate *scopePrivate = 0;
if (scope) {
scopePrivate = QQuickItemPrivate::get(scope);
if ( !scopePrivate->subFocusItem )
return;//No focus, nothing to do.
}
613
614
615
QQuickItem *oldActiveFocusItem = 0;
QQuickItem *newActiveFocusItem = 0;
616
617
QVarLengthArray<QQuickItem *, 20> changed;
618
619
Q_ASSERT(item == rootItem || item == scopePrivate->subFocusItem);
620
621
// Does this change the active focus?
622
if (item == rootItem || scopePrivate->activeFocus) {
623
624
oldActiveFocusItem = activeFocusItem;
newActiveFocusItem = scope;
625
626
627
628
Q_ASSERT(oldActiveFocusItem);
#ifndef QT_NO_IM
629
qApp->inputMethod()->reset();
630
631
632
633
634
635
#endif
activeFocusItem = 0;
QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
q->sendEvent(oldActiveFocusItem, &event);
636
QQuickItem *afi = oldActiveFocusItem;
637
while (afi != scope) {
638
639
if (QQuickItemPrivate::get(afi)->activeFocus) {
QQuickItemPrivate::get(afi)->activeFocus = false;
640
641
642
643
644
645
changed << afi;
}
afi = afi->parentItem();
}
}
646
if (item != rootItem && !(options & DontChangeSubFocusItem)) {
647
QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
648
if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
649
QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
650
651
changed << oldSubFocusItem;
}
652
653
654
QQuickItemPrivate::get(item)->updateSubFocusItem(scope, false);
655
} else if (!(options & DontChangeFocusProperty)) {
656
QQuickItemPrivate::get(item)->focus = false;
657
changed << item;
658
659
660
661
662
663
664
}
if (newActiveFocusItem) {
Q_ASSERT(newActiveFocusItem == scope);
activeFocusItem = scope;
QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
665
q->sendEvent(newActiveFocusItem, &event);
666
667
}
668
669
emit q->focusObjectChanged(activeFocusItem);
670
if (!changed.isEmpty())
671
672
673
notifyFocusChangesRecur(changed.data(), changed.count() - 1);
}
674
void QQuickCanvasPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining)
675
{
676
QQmlGuard<QQuickItem> item(*items);
677
678
679
680
681
if (remaining)
notifyFocusChangesRecur(items + 1, remaining - 1);
if (item) {
682
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
683
684
685
686
687
688
689
690
if (itemPrivate->notifiedFocus != itemPrivate->focus) {
itemPrivate->notifiedFocus = itemPrivate->focus;
emit item->focusChanged(itemPrivate->focus);
}
if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
691
itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
692
693
emit item->activeFocusChanged(itemPrivate->activeFocus);
}
694
}
695
696
}
697
void QQuickCanvasPrivate::dirtyItem(QQuickItem *)
698
{
699
Q_Q(QQuickCanvas);
700
701
702
q->maybeUpdate();
}
703
void QQuickCanvasPrivate::cleanup(QSGNode *n)
704
{
705
Q_Q(QQuickCanvas);
706
707
708
709
710
711
712
Q_ASSERT(!cleanupNodeList.contains(n));
cleanupNodeList.append(n);
q->maybeUpdate();
}
713
714
715
716
717
718
719
720
/*!
\qmlclass Window QQuickCanvas
\inqmlmodule QtQuick.Window 2
\brief The Window object creates a new top-level window.
The Window object creates a new top-level window for a QtQuick scene. It automatically sets up the
window for use with QtQuick 2.0 graphical elements.
*/
721
722
723
724
725
726
727
728
729
730
731
732
/*!
\class QQuickCanvas
\since QtQuick 2.0
\brief The QQuickCanvas class provides the canvas for displaying a graphical QML scene
QQuickCanvas provides the graphical scene management needed to interact with and display
a scene of QQuickItems.
A QQuickCanvas always has a single invisible root item. To add items to this canvas,
reparent the items to the root item or to an existing item in the scene.
For easily displaying a scene from a QML file, see \l{QQuickView}.
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
\section1 Scene Graph and Rendering
The QQuickCanvas uses a scene graph on top of OpenGL to render. This scene graph is disconnected
from the QML scene and potentially lives in another thread, depending on the platform
implementation. Since the rendering scene graph lives independently from the QML scene, it can
also be completely released without affecting the state of the QML scene.
The sceneGraphInitialized() signal is emitted on the rendering thread before the QML scene is
rendered to the screen for the first time. If the rendering scene graph has been released
the signal will be emitted again before the next frame is rendered.
Rendering is done by first copying the QML scene's state into the rendering scene graph. This is
done by calling QQuickItem::updatePaintNode() functions on all items that have changed. This phase
is run on the rendering thread with the GUI thread blocked, when a separate rendering thread
is being used. The scene can then be rendered.
Before the scene graph is rendered, the beforeRendering() signal is emitted. The OpenGL context
is bound at this point and the application is free to do its own rendering. Also
make sure to disable the clearing of the color buffer, using setClearBeforeRendering(). The
default clear color is white and can be changed with setClearColor(). After the scene has
been rendered, the afterRendering() signal is emitted. The application can use this to render
OpenGL on top of a QML application. Once the frame is fully done and has been swapped,
the frameSwapped() signal is emitted.
While the scene graph is being rendered on the rendering thread, the GUI will process animations
for the next frame. This means that as long as users are not using scene graph API
directly, the added complexity of a rendering thread can be completely ignored.
When a QQuickCanvas is programatically hidden with hide() or setVisible(false), it will
stop rendering and its scene graph and OpenGL context might be released. The
sceneGraphInvalidated() signal will be emitted when this happens.
\warning It is crucial that OpenGL operations and interaction with the scene graph happens
exclusively on the rendering thread, primarily during the updatePaintNode() phase.
\warning As signals related to rendering might be emitted from the rendering thread,
connections should be made using Qt::DirectConnection
\section1 Resource Management
QML will typically try to cache images, scene graph nodes, etc to improve performance, but in
some low-memory scenarios it might be required to aggressively release these resources. The
releaseResources() can be used to force clean up of certain resources. Calling releaseResources()
may result in the entire scene graph and its OpenGL context being deleted. The
sceneGraphInvalidated() signal will be emitted when this happens.
782
*/
783
784
QQuickCanvas::QQuickCanvas(QWindow *parent)
: QWindow(*(new QQuickCanvasPrivate), parent)
785
{
786
Q_D(QQuickCanvas);
787
788
789
d->init(this);
}
790
QQuickCanvas::QQuickCanvas(QQuickCanvasPrivate &dd, QWindow *parent)
791
: QWindow(dd, parent)
792
{
793
Q_D(QQuickCanvas);
794
795
796
d->init(this);
}
797
QQuickCanvas::~QQuickCanvas()
798
{
799
Q_D(QQuickCanvas);
800
801
d->windowManager->canvasDestroyed(this);
802
803
// ### should we change ~QQuickItem to handle this better?
804
// manually cleanup for the root item (item destructor only handles these when an item is parented)
805
QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(d->rootItem);
806
807
rootItemPrivate->removeFromDirtyList();
808
QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
809
810
delete d->incubationController; d->incubationController = 0;
811
812
813
delete d->rootItem; d->rootItem = 0;
}
814
815
816
817
/*!
This function tries to release redundant resources currently held by the QML scene.
818
819
820
821
822
823
824
825
826
Calling this function might result in the scene graph and the OpenGL context used
for rendering being released to release graphics memory. If this happens, the
sceneGraphInvalidated() signal will be called, allowing users to clean up their
own graphics resources. The setPersistentOpenGLContext() and setPersistentSceneGraph()
functions can be used to prevent this from happening, if handling the cleanup is
not feasible in the application, at the cost of higher memory usage.
\sa sceneGraphInvalidated(), setPersistentOpenGLContext(), setPersistentSceneGraph().
827
828
829
830
831
832
*/
void QQuickCanvas::releaseResources()
{
Q_D(QQuickCanvas);
d->windowManager->releaseResources();
833
QQuickPixmap::purgeCache();
834
835
836
837
}
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
/*!
Controls whether the OpenGL context can be released as a part of a call to
releaseResources().
The OpenGL context might still be released when the user makes an explicit
call to hide().
\sa setPersistentSceneGraph()
*/
void QQuickCanvas::setPersistentOpenGLContext(bool persistent)
{
Q_D(QQuickCanvas);
d->persistentGLContext = persistent;
}
/*!
Returns whether the OpenGL context can be released as a part of a call to
releaseResources().
*/
bool QQuickCanvas::isPersistentOpenGLContext() const
{
Q_D(const QQuickCanvas);
return d->persistentGLContext;
}
/*!
Controls whether the scene graph nodes and resources can be released as a
part of a call to releaseResources().
The scene graph nodes and resources might still be released when the user
makes an explicit call to hide().
\sa setPersistentOpenGLContext()
*/
void QQuickCanvas::setPersistentSceneGraph(bool persistent)
{
Q_D(QQuickCanvas);
d->persistentSceneGraph = persistent;
}
/*!
Returns whether the scene graph nodes and resources can be released as a part
of a call to releaseResources().
*/
bool QQuickCanvas::isPersistentSceneGraph() const
{
Q_D(const QQuickCanvas);
return d->persistentSceneGraph;
}
901
902
903
904
905
906
/*!
Returns the invisible root item of the scene.
A QQuickCanvas always has a single invisible root item. To add items to this canvas,
reparent the items to the root item or to an existing item in the scene.
*/
907
QQuickItem *QQuickCanvas::rootItem() const
908
{
909
Q_D(const QQuickCanvas);
910
911
912
913
return d->rootItem;
}
914
915
916
/*!
Returns the item which currently has active focus.
*/
917
QQuickItem *QQuickCanvas::activeFocusItem() const
918
{
919
Q_D(const QQuickCanvas);
920
921
922
923
return d->activeFocusItem;
}
924
925
926
927
928
929
930
931
932
933
QObject *QQuickCanvas::focusObject() const
{
Q_D(const QQuickCanvas);
if (d->activeFocusItem)
return d->activeFocusItem;
return const_cast<QQuickCanvas*>(this);
}
934
935
936
/*!
Returns the item which currently has the mouse grab.
*/
937
QQuickItem *QQuickCanvas::mouseGrabberItem() const
938
{
939
Q_D(const QQuickCanvas);
940
941
942
943
944
return d->mouseGrabberItem;
}
945
946
947
948
949
950
951
952
/*!
\qmlproperty color QtQuick2.Window::Window::color
The background color for the window.
Setting this property is more efficient than using a separate Rectangle.
*/
953
bool QQuickCanvasPrivate::clearHover()
954
{
955
956
if (hoverItems.isEmpty())
return false;
957
958
QPointF pos = QCursor::pos(); // ### refactor: q->mapFromGlobal(QCursor::pos());
959
960
bool accepted = false;
961
foreach (QQuickItem* item, hoverItems)
962
accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), true) || accepted;
963
964
hoverItems.clear();
return accepted;
965
966
}
967
/*! \reimp */
968
bool QQuickCanvas::event(QEvent *e)
969
{
970
Q_D(QQuickCanvas);
971
972
973
974
975
976
switch (e->type()) {
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
977
case QEvent::TouchCancel:
978
979
980
981
{
QTouchEvent *touch = static_cast<QTouchEvent *>(e);
d->translateTouchEvent(touch);
d->deliverTouchEvent(touch);
982
983
if (qmlTranslateTouchToMouse())
d->translateTouchToMouse(touch);
984
985
return touch->isAccepted();
986
987
988
989
990
}
case QEvent::Leave:
d->clearHover();
d->lastMousePosition = QPoint();
break;
991
992
993
994
995
case QEvent::DragEnter:
case QEvent::DragLeave:
case QEvent::DragMove:
case QEvent::Drop:
d->deliverDragEvent(&d->dragGrabber, e);
996
break;
997
998
999
case QEvent::WindowDeactivate:
rootItem()->windowDeactivateEvent();
break;
1000
default: