source: trunk/src/declarative/graphicsitems/qdeclarativeflickable.cpp@ 1147

Last change on this file since 1147 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 51.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtDeclarative module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "private/qdeclarativeflickable_p.h"
43#include "private/qdeclarativeflickable_p_p.h"
44#include <qdeclarativeinfo.h>
45#include <QGraphicsSceneMouseEvent>
46#include <QPointer>
47#include <QTimer>
48
49QT_BEGIN_NAMESPACE
50
51
52// FlickThreshold determines how far the "mouse" must have moved
53// before we perform a flick.
54static const int FlickThreshold = 20;
55
56QDeclarativeFlickableVisibleArea::QDeclarativeFlickableVisibleArea(QDeclarativeFlickable *parent)
57 : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.)
58 , m_yPosition(0.), m_heightRatio(0.)
59{
60}
61
62qreal QDeclarativeFlickableVisibleArea::widthRatio() const
63{
64 return m_widthRatio;
65}
66
67qreal QDeclarativeFlickableVisibleArea::xPosition() const
68{
69 return m_xPosition;
70}
71
72qreal QDeclarativeFlickableVisibleArea::heightRatio() const
73{
74 return m_heightRatio;
75}
76
77qreal QDeclarativeFlickableVisibleArea::yPosition() const
78{
79 return m_yPosition;
80}
81
82void QDeclarativeFlickableVisibleArea::updateVisible()
83{
84 QDeclarativeFlickablePrivate *p = static_cast<QDeclarativeFlickablePrivate *>(QGraphicsItemPrivate::get(flickable));
85
86 bool changeX = false;
87 bool changeY = false;
88 bool changeWidth = false;
89 bool changeHeight = false;
90
91 // Vertical
92 const qreal viewheight = flickable->height();
93 const qreal maxyextent = -flickable->maxYExtent() + flickable->minYExtent();
94 qreal pagePos = (-p->vData.move.value() + flickable->minYExtent()) / (maxyextent + viewheight);
95 qreal pageSize = viewheight / (maxyextent + viewheight);
96
97 if (pageSize != m_heightRatio) {
98 m_heightRatio = pageSize;
99 changeHeight = true;
100 }
101 if (pagePos != m_yPosition) {
102 m_yPosition = pagePos;
103 changeY = true;
104 }
105
106 // Horizontal
107 const qreal viewwidth = flickable->width();
108 const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent();
109 pagePos = (-p->hData.move.value() + flickable->minXExtent()) / (maxxextent + viewwidth);
110 pageSize = viewwidth / (maxxextent + viewwidth);
111
112 if (pageSize != m_widthRatio) {
113 m_widthRatio = pageSize;
114 changeWidth = true;
115 }
116 if (pagePos != m_xPosition) {
117 m_xPosition = pagePos;
118 changeX = true;
119 }
120
121 if (changeX)
122 emit xPositionChanged(m_xPosition);
123 if (changeY)
124 emit yPositionChanged(m_yPosition);
125 if (changeWidth)
126 emit widthRatioChanged(m_widthRatio);
127 if (changeHeight)
128 emit heightRatioChanged(m_heightRatio);
129}
130
131
132QDeclarativeFlickablePrivate::QDeclarativeFlickablePrivate()
133 : contentItem(new QDeclarativeItem)
134 , hData(this, &QDeclarativeFlickablePrivate::setRoundedViewportX)
135 , vData(this, &QDeclarativeFlickablePrivate::setRoundedViewportY)
136 , flickingHorizontally(false), flickingVertically(false)
137 , hMoved(false), vMoved(false)
138 , movingHorizontally(false), movingVertically(false)
139 , stealMouse(false), pressed(false), interactive(true), calcVelocity(false)
140 , deceleration(500), maxVelocity(2000), reportedVelocitySmoothing(100)
141 , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(600)
142 , vTime(0), visibleArea(0)
143 , flickableDirection(QDeclarativeFlickable::AutoFlickDirection)
144 , boundsBehavior(QDeclarativeFlickable::DragAndOvershootBounds)
145{
146}
147
148void QDeclarativeFlickablePrivate::init()
149{
150 Q_Q(QDeclarativeFlickable);
151 QDeclarative_setParent_noEvent(contentItem, q);
152 contentItem->setParentItem(q);
153 static int timelineUpdatedIdx = -1;
154 static int timelineCompletedIdx = -1;
155 static int flickableTickedIdx = -1;
156 static int flickableMovementEndingIdx = -1;
157 if (timelineUpdatedIdx == -1) {
158 timelineUpdatedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("updated()");
159 timelineCompletedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("completed()");
160 flickableTickedIdx = QDeclarativeFlickable::staticMetaObject.indexOfSlot("ticked()");
161 flickableMovementEndingIdx = QDeclarativeFlickable::staticMetaObject.indexOfSlot("movementEnding()");
162 }
163 QMetaObject::connect(&timeline, timelineUpdatedIdx,
164 q, flickableTickedIdx, Qt::DirectConnection);
165 QMetaObject::connect(&timeline, timelineCompletedIdx,
166 q, flickableMovementEndingIdx, Qt::DirectConnection);
167 q->setAcceptedMouseButtons(Qt::LeftButton);
168 q->setFiltersChildEvents(true);
169 QDeclarativeItemPrivate *viewportPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(contentItem));
170 viewportPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
171 lastPosTime.invalidate();
172}
173
174/*
175 Returns the amount to overshoot by given a velocity.
176 Will be roughly in range 0 - size/4
177*/
178qreal QDeclarativeFlickablePrivate::overShootDistance(qreal velocity, qreal size)
179{
180 if (maxVelocity <= 0)
181 return 0.0;
182
183 velocity = qAbs(velocity);
184 if (velocity > maxVelocity)
185 velocity = maxVelocity;
186 qreal dist = size / 4 * velocity / maxVelocity;
187 return dist;
188}
189
190void QDeclarativeFlickablePrivate::itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeom, const QRectF &oldGeom)
191{
192 Q_Q(QDeclarativeFlickable);
193 if (item == contentItem) {
194 if (newGeom.x() != oldGeom.x())
195 emit q->contentXChanged();
196 if (newGeom.y() != oldGeom.y())
197 emit q->contentYChanged();
198 }
199}
200
201void QDeclarativeFlickablePrivate::flickX(qreal velocity)
202{
203 Q_Q(QDeclarativeFlickable);
204 flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity);
205}
206
207void QDeclarativeFlickablePrivate::flickY(qreal velocity)
208{
209 Q_Q(QDeclarativeFlickable);
210 flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity);
211}
212
213void QDeclarativeFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
214 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
215{
216 Q_Q(QDeclarativeFlickable);
217 qreal maxDistance = -1;
218 bool overShoot = boundsBehavior == QDeclarativeFlickable::DragAndOvershootBounds;
219 // -ve velocity means list is moving up
220 if (velocity > 0) {
221 if (data.move.value() < minExtent)
222 maxDistance = qAbs(minExtent - data.move.value() + (overShoot?overShootDistance(velocity,vSize):0));
223 data.flickTarget = minExtent;
224 } else {
225 if (data.move.value() > maxExtent)
226 maxDistance = qAbs(maxExtent - data.move.value()) + (overShoot?overShootDistance(velocity,vSize):0);
227 data.flickTarget = maxExtent;
228 }
229 if (maxDistance > 0) {
230 qreal v = velocity;
231 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
232 if (v < 0)
233 v = -maxVelocity;
234 else
235 v = maxVelocity;
236 }
237 timeline.reset(data.move);
238 timeline.accel(data.move, v, deceleration, maxDistance);
239 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
240 if (!flickingHorizontally && q->xflick()) {
241 flickingHorizontally = true;
242 emit q->flickingChanged();
243 emit q->flickingHorizontallyChanged();
244 if (!flickingVertically)
245 emit q->flickStarted();
246 }
247 if (!flickingVertically && q->yflick()) {
248 flickingVertically = true;
249 emit q->flickingChanged();
250 emit q->flickingVerticallyChanged();
251 if (!flickingHorizontally)
252 emit q->flickStarted();
253 }
254 } else {
255 timeline.reset(data.move);
256 fixup(data, minExtent, maxExtent);
257 }
258}
259
260void QDeclarativeFlickablePrivate::fixupY_callback(void *data)
261{
262 ((QDeclarativeFlickablePrivate *)data)->fixupY();
263}
264
265void QDeclarativeFlickablePrivate::fixupX_callback(void *data)
266{
267 ((QDeclarativeFlickablePrivate *)data)->fixupX();
268}
269
270void QDeclarativeFlickablePrivate::fixupX()
271{
272 Q_Q(QDeclarativeFlickable);
273 fixup(hData, q->minXExtent(), q->maxXExtent());
274}
275
276void QDeclarativeFlickablePrivate::fixupY()
277{
278 Q_Q(QDeclarativeFlickable);
279 fixup(vData, q->minYExtent(), q->maxYExtent());
280}
281
282void QDeclarativeFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
283{
284 if (data.move.value() > minExtent || maxExtent > minExtent) {
285 timeline.reset(data.move);
286 if (data.move.value() != minExtent) {
287 if (fixupDuration) {
288 qreal dist = minExtent - data.move;
289 timeline.move(data.move, minExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
290 timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
291 } else {
292 timeline.set(data.move, minExtent);
293 }
294 }
295 } else if (data.move.value() < maxExtent) {
296 timeline.reset(data.move);
297 if (fixupDuration) {
298 qreal dist = maxExtent - data.move;
299 timeline.move(data.move, maxExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
300 timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
301 } else {
302 timeline.set(data.move, maxExtent);
303 }
304 }
305 vTime = timeline.time();
306}
307
308void QDeclarativeFlickablePrivate::updateBeginningEnd()
309{
310 Q_Q(QDeclarativeFlickable);
311 bool atBoundaryChange = false;
312
313 // Vertical
314 const int maxyextent = int(-q->maxYExtent());
315 const qreal ypos = -vData.move.value();
316 bool atBeginning = (ypos <= -q->minYExtent());
317 bool atEnd = (maxyextent <= ypos);
318
319 if (atBeginning != vData.atBeginning) {
320 vData.atBeginning = atBeginning;
321 atBoundaryChange = true;
322 }
323 if (atEnd != vData.atEnd) {
324 vData.atEnd = atEnd;
325 atBoundaryChange = true;
326 }
327
328 // Horizontal
329 const int maxxextent = int(-q->maxXExtent());
330 const qreal xpos = -hData.move.value();
331 atBeginning = (xpos <= -q->minXExtent());
332 atEnd = (maxxextent <= xpos);
333
334 if (atBeginning != hData.atBeginning) {
335 hData.atBeginning = atBeginning;
336 atBoundaryChange = true;
337 }
338 if (atEnd != hData.atEnd) {
339 hData.atEnd = atEnd;
340 atBoundaryChange = true;
341 }
342
343 if (atBoundaryChange)
344 emit q->isAtBoundaryChanged();
345
346 if (visibleArea)
347 visibleArea->updateVisible();
348}
349
350/*!
351 \qmlclass Flickable QDeclarativeFlickable
352 \since 4.7
353 \ingroup qml-basic-interaction-elements
354
355 \brief The Flickable item provides a surface that can be "flicked".
356 \inherits Item
357
358 The Flickable item places its children on a surface that can be dragged
359 and flicked, causing the view onto the child items to scroll. This
360 behavior forms the basis of Items that are designed to show large numbers
361 of child items, such as \l ListView and \l GridView.
362
363 In traditional user interfaces, views can be scrolled using standard
364 controls, such as scroll bars and arrow buttons. In some situations, it
365 is also possible to drag the view directly by pressing and holding a
366 mouse button while moving the cursor. In touch-based user interfaces,
367 this dragging action is often complemented with a flicking action, where
368 scrolling continues after the user has stopped touching the view.
369
370 Flickable does not automatically clip its contents. If it is not used as
371 a full-screen item, you should consider setting the \l{Item::}{clip} property
372 to true.
373
374 \section1 Example Usage
375
376 \beginfloatright
377 \inlineimage flickable.gif
378 \endfloat
379
380 The following example shows a small view onto a large image in which the
381 user can drag or flick the image in order to view different parts of it.
382
383 \snippet doc/src/snippets/declarative/flickable.qml document
384
385 \clearfloat
386
387 Items declared as children of a Flickable are automatically parented to the
388 Flickable's \l contentItem. This should be taken into account when
389 operating on the children of the Flickable; it is usually the children of
390 \c contentItem that are relevant. For example, the bound of Items added
391 to the Flickable will be available by \c contentItem.childrenRect
392
393 \section1 Limitations
394
395 \note Due to an implementation detail, items placed inside a Flickable cannot anchor to it by
396 \c id. Use \c parent instead.
397*/
398
399/*!
400 \qmlsignal Flickable::onMovementStarted()
401
402 This handler is called when the view begins moving due to user
403 interaction.
404*/
405
406/*!
407 \qmlsignal Flickable::onMovementEnded()
408
409 This handler is called when the view stops moving due to user
410 interaction. If a flick was generated, this handler will
411 be triggered once the flick stops. If a flick was not
412 generated, the handler will be triggered when the
413 user stops dragging - i.e. a mouse or touch release.
414*/
415
416/*!
417 \qmlsignal Flickable::onFlickStarted()
418
419 This handler is called when the view is flicked. A flick
420 starts from the point that the mouse or touch is released,
421 while still in motion.
422*/
423
424/*!
425 \qmlsignal Flickable::onFlickEnded()
426
427 This handler is called when the view stops moving due to a flick.
428*/
429
430/*!
431 \qmlproperty real Flickable::visibleArea.xPosition
432 \qmlproperty real Flickable::visibleArea.widthRatio
433 \qmlproperty real Flickable::visibleArea.yPosition
434 \qmlproperty real Flickable::visibleArea.heightRatio
435
436 These properties describe the position and size of the currently viewed area.
437 The size is defined as the percentage of the full view currently visible,
438 scaled to 0.0 - 1.0. The page position is usually in the range 0.0 (beginning) to
439 1.0 minus size ratio (end), i.e. \c yPosition is in the range 0.0 to 1.0-\c heightRatio.
440 However, it is possible for the contents to be dragged outside of the normal
441 range, resulting in the page positions also being outside the normal range.
442
443 These properties are typically used to draw a scrollbar. For example:
444
445 \snippet doc/src/snippets/declarative/flickableScrollbar.qml 0
446 \dots 8
447 \snippet doc/src/snippets/declarative/flickableScrollbar.qml 1
448
449 \sa {declarative/ui-components/scrollbar}{scrollbar example}
450*/
451
452QDeclarativeFlickable::QDeclarativeFlickable(QDeclarativeItem *parent)
453 : QDeclarativeItem(*(new QDeclarativeFlickablePrivate), parent)
454{
455 Q_D(QDeclarativeFlickable);
456 d->init();
457}
458
459QDeclarativeFlickable::QDeclarativeFlickable(QDeclarativeFlickablePrivate &dd, QDeclarativeItem *parent)
460 : QDeclarativeItem(dd, parent)
461{
462 Q_D(QDeclarativeFlickable);
463 d->init();
464}
465
466QDeclarativeFlickable::~QDeclarativeFlickable()
467{
468}
469
470/*!
471 \qmlproperty real Flickable::contentX
472 \qmlproperty real Flickable::contentY
473
474 These properties hold the surface coordinate currently at the top-left
475 corner of the Flickable. For example, if you flick an image up 100 pixels,
476 \c contentY will be 100.
477*/
478qreal QDeclarativeFlickable::contentX() const
479{
480 Q_D(const QDeclarativeFlickable);
481 return -d->contentItem->x();
482}
483
484void QDeclarativeFlickable::setContentX(qreal pos)
485{
486 Q_D(QDeclarativeFlickable);
487 d->timeline.reset(d->hData.move);
488 d->vTime = d->timeline.time();
489 movementXEnding();
490 if (-pos != d->hData.move.value()) {
491 d->hData.move.setValue(-pos);
492 viewportMoved();
493 }
494}
495
496qreal QDeclarativeFlickable::contentY() const
497{
498 Q_D(const QDeclarativeFlickable);
499 return -d->contentItem->y();
500}
501
502void QDeclarativeFlickable::setContentY(qreal pos)
503{
504 Q_D(QDeclarativeFlickable);
505 d->timeline.reset(d->vData.move);
506 d->vTime = d->timeline.time();
507 movementYEnding();
508 if (-pos != d->vData.move.value()) {
509 d->vData.move.setValue(-pos);
510 viewportMoved();
511 }
512}
513
514/*!
515 \qmlproperty bool Flickable::interactive
516
517 This property describes whether the user can interact with the Flickable.
518 A user cannot drag or flick a Flickable that is not interactive.
519
520 By default, this property is true.
521
522 This property is useful for temporarily disabling flicking. This allows
523 special interaction with Flickable's children; for example, you might want
524 to freeze a flickable map while scrolling through a pop-up dialog that
525 is a child of the Flickable.
526*/
527bool QDeclarativeFlickable::isInteractive() const
528{
529 Q_D(const QDeclarativeFlickable);
530 return d->interactive;
531}
532
533void QDeclarativeFlickable::setInteractive(bool interactive)
534{
535 Q_D(QDeclarativeFlickable);
536 if (interactive != d->interactive) {
537 d->interactive = interactive;
538 if (!interactive && (d->flickingHorizontally || d->flickingVertically)) {
539 d->timeline.clear();
540 d->vTime = d->timeline.time();
541 d->flickingHorizontally = false;
542 d->flickingVertically = false;
543 emit flickingChanged();
544 emit flickingHorizontallyChanged();
545 emit flickingVerticallyChanged();
546 emit flickEnded();
547 }
548 emit interactiveChanged();
549 }
550}
551
552/*!
553 \qmlproperty real Flickable::horizontalVelocity
554 \qmlproperty real Flickable::verticalVelocity
555
556 The instantaneous velocity of movement along the x and y axes, in pixels/sec.
557
558 The reported velocity is smoothed to avoid erratic output.
559*/
560qreal QDeclarativeFlickable::horizontalVelocity() const
561{
562 Q_D(const QDeclarativeFlickable);
563 return d->hData.smoothVelocity.value();
564}
565
566qreal QDeclarativeFlickable::verticalVelocity() const
567{
568 Q_D(const QDeclarativeFlickable);
569 return d->vData.smoothVelocity.value();
570}
571
572/*!
573 \qmlproperty bool Flickable::atXBeginning
574 \qmlproperty bool Flickable::atXEnd
575 \qmlproperty bool Flickable::atYBeginning
576 \qmlproperty bool Flickable::atYEnd
577
578 These properties are true if the flickable view is positioned at the beginning,
579 or end respecively.
580*/
581bool QDeclarativeFlickable::isAtXEnd() const
582{
583 Q_D(const QDeclarativeFlickable);
584 return d->hData.atEnd;
585}
586
587bool QDeclarativeFlickable::isAtXBeginning() const
588{
589 Q_D(const QDeclarativeFlickable);
590 return d->hData.atBeginning;
591}
592
593bool QDeclarativeFlickable::isAtYEnd() const
594{
595 Q_D(const QDeclarativeFlickable);
596 return d->vData.atEnd;
597}
598
599bool QDeclarativeFlickable::isAtYBeginning() const
600{
601 Q_D(const QDeclarativeFlickable);
602 return d->vData.atBeginning;
603}
604
605void QDeclarativeFlickable::ticked()
606{
607 viewportMoved();
608}
609
610/*!
611 \qmlproperty Item Flickable::contentItem
612
613 The internal item that contains the Items to be moved in the Flickable.
614
615 Items declared as children of a Flickable are automatically parented to the Flickable's contentItem.
616
617 Items created dynamically need to be explicitly parented to the \e contentItem:
618 \code
619 Flickable {
620 id: myFlickable
621 function addItem(file) {
622 var component = Qt.createComponent(file)
623 component.createObject(myFlickable.contentItem);
624 }
625 }
626 \endcode
627*/
628QDeclarativeItem *QDeclarativeFlickable::contentItem()
629{
630 Q_D(QDeclarativeFlickable);
631 return d->contentItem;
632}
633
634QDeclarativeFlickableVisibleArea *QDeclarativeFlickable::visibleArea()
635{
636 Q_D(QDeclarativeFlickable);
637 if (!d->visibleArea)
638 d->visibleArea = new QDeclarativeFlickableVisibleArea(this);
639 return d->visibleArea;
640}
641
642/*!
643 \qmlproperty enumeration Flickable::flickableDirection
644
645 This property determines which directions the view can be flicked.
646
647 \list
648 \o Flickable.AutoFlickDirection (default) - allows flicking vertically if the
649 \e contentHeight is not equal to the \e height of the Flickable.
650 Allows flicking horizontally if the \e contentWidth is not equal
651 to the \e width of the Flickable.
652 \o Flickable.HorizontalFlick - allows flicking horizontally.
653 \o Flickable.VerticalFlick - allows flicking vertically.
654 \o Flickable.HorizontalAndVerticalFlick - allows flicking in both directions.
655 \endlist
656*/
657QDeclarativeFlickable::FlickableDirection QDeclarativeFlickable::flickableDirection() const
658{
659 Q_D(const QDeclarativeFlickable);
660 return d->flickableDirection;
661}
662
663void QDeclarativeFlickable::setFlickableDirection(FlickableDirection direction)
664{
665 Q_D(QDeclarativeFlickable);
666 if (direction != d->flickableDirection) {
667 d->flickableDirection = direction;
668 emit flickableDirectionChanged();
669 }
670}
671
672void QDeclarativeFlickablePrivate::handleMousePressEvent(QGraphicsSceneMouseEvent *event)
673{
674 Q_Q(QDeclarativeFlickable);
675 if (interactive && timeline.isActive() && (qAbs(hData.velocity) > 10 || qAbs(vData.velocity) > 10))
676 stealMouse = true; // If we've been flicked then steal the click.
677 else
678 stealMouse = false;
679 q->setKeepMouseGrab(stealMouse);
680 pressed = true;
681 timeline.clear();
682 hData.velocity = 0;
683 vData.velocity = 0;
684 hData.dragStartOffset = 0;
685 vData.dragStartOffset = 0;
686 lastPos = QPoint();
687 QDeclarativeItemPrivate::start(lastPosTime);
688 pressPos = event->pos();
689 hData.pressPos = hData.move.value();
690 vData.pressPos = vData.move.value();
691 flickingHorizontally = false;
692 flickingVertically = false;
693 QDeclarativeItemPrivate::start(pressTime);
694 QDeclarativeItemPrivate::start(velocityTime);
695}
696
697void QDeclarativeFlickablePrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event)
698{
699 Q_Q(QDeclarativeFlickable);
700 if (!interactive || !lastPosTime.isValid())
701 return;
702 bool rejectY = false;
703 bool rejectX = false;
704
705 bool stealY = stealMouse;
706 bool stealX = stealMouse;
707
708 if (q->yflick()) {
709 int dy = int(event->pos().y() - pressPos.y());
710 if (qAbs(dy) > QApplication::startDragDistance() || QDeclarativeItemPrivate::elapsed(pressTime) > 200) {
711 if (!vMoved)
712 vData.dragStartOffset = dy;
713 qreal newY = dy + vData.pressPos - vData.dragStartOffset;
714 const qreal minY = q->minYExtent();
715 const qreal maxY = q->maxYExtent();
716 if (newY > minY)
717 newY = minY + (newY - minY) / 2;
718 if (newY < maxY && maxY - minY <= 0)
719 newY = maxY + (newY - maxY) / 2;
720 if (boundsBehavior == QDeclarativeFlickable::StopAtBounds && (newY > minY || newY < maxY)) {
721 rejectY = true;
722 if (newY < maxY) {
723 newY = maxY;
724 rejectY = false;
725 }
726 if (newY > minY) {
727 newY = minY;
728 rejectY = false;
729 }
730 }
731 if (!rejectY && stealMouse) {
732 vData.move.setValue(qRound(newY));
733 vMoved = true;
734 }
735 if (qAbs(dy) > QApplication::startDragDistance())
736 stealY = true;
737 }
738 }
739
740 if (q->xflick()) {
741 int dx = int(event->pos().x() - pressPos.x());
742 if (qAbs(dx) > QApplication::startDragDistance() || QDeclarativeItemPrivate::elapsed(pressTime) > 200) {
743 if (!hMoved)
744 hData.dragStartOffset = dx;
745 qreal newX = dx + hData.pressPos - hData.dragStartOffset;
746 const qreal minX = q->minXExtent();
747 const qreal maxX = q->maxXExtent();
748 if (newX > minX)
749 newX = minX + (newX - minX) / 2;
750 if (newX < maxX && maxX - minX <= 0)
751 newX = maxX + (newX - maxX) / 2;
752 if (boundsBehavior == QDeclarativeFlickable::StopAtBounds && (newX > minX || newX < maxX)) {
753 rejectX = true;
754 if (newX < maxX) {
755 newX = maxX;
756 rejectX = false;
757 }
758 if (newX > minX) {
759 newX = minX;
760 rejectX = false;
761 }
762 }
763 if (!rejectX && stealMouse) {
764 hData.move.setValue(qRound(newX));
765 hMoved = true;
766 }
767
768 if (qAbs(dx) > QApplication::startDragDistance())
769 stealX = true;
770 }
771 }
772
773 stealMouse = stealX || stealY;
774 if (stealMouse)
775 q->setKeepMouseGrab(true);
776
777 if (!lastPos.isNull()) {
778 qreal elapsed = qreal(QDeclarativeItemPrivate::restart(lastPosTime)) / 1000.;
779 if (elapsed <= 0)
780 elapsed = 1;
781 if (q->yflick()) {
782 qreal diff = event->pos().y() - lastPos.y();
783 // average to reduce the effect of spurious moves
784 vData.velocity += diff / elapsed;
785 vData.velocity /= 2;
786 }
787
788 if (q->xflick()) {
789 qreal diff = event->pos().x() - lastPos.x();
790 // average to reduce the effect of spurious moves
791 hData.velocity += diff / elapsed;
792 hData.velocity /= 2;
793 }
794 }
795
796 if (rejectY) vData.velocity = 0;
797 if (rejectX) hData.velocity = 0;
798
799 if (hMoved || vMoved) {
800 q->movementStarting();
801 q->viewportMoved();
802 }
803
804 lastPos = event->pos();
805}
806
807void QDeclarativeFlickablePrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *event)
808{
809 Q_Q(QDeclarativeFlickable);
810 stealMouse = false;
811 q->setKeepMouseGrab(false);
812 pressed = false;
813 if (!lastPosTime.isValid())
814 return;
815
816 if (QDeclarativeItemPrivate::elapsed(lastPosTime) > 100) {
817 // if we drag then pause before release we should not cause a flick.
818 hData.velocity = 0.0;
819 vData.velocity = 0.0;
820 }
821
822 vTime = timeline.time();
823 if (qAbs(vData.velocity) > MinimumFlickVelocity && qAbs(event->pos().y() - pressPos.y()) > FlickThreshold)
824 flickY(vData.velocity);
825 else
826 fixupY();
827
828 if (qAbs(hData.velocity) > MinimumFlickVelocity && qAbs(event->pos().x() - pressPos.x()) > FlickThreshold)
829 flickX(hData.velocity);
830 else
831 fixupX();
832
833 lastPosTime.invalidate();
834
835 if (!timeline.isActive())
836 q->movementEnding();
837}
838
839void QDeclarativeFlickable::mousePressEvent(QGraphicsSceneMouseEvent *event)
840{
841 Q_D(QDeclarativeFlickable);
842 if (d->interactive) {
843 d->handleMousePressEvent(event);
844 event->accept();
845 } else {
846 QDeclarativeItem::mousePressEvent(event);
847 }
848}
849
850void QDeclarativeFlickable::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
851{
852 Q_D(QDeclarativeFlickable);
853 if (d->interactive) {
854 d->handleMouseMoveEvent(event);
855 event->accept();
856 } else {
857 QDeclarativeItem::mouseMoveEvent(event);
858 }
859}
860
861void QDeclarativeFlickable::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
862{
863 Q_D(QDeclarativeFlickable);
864 if (d->interactive) {
865 d->clearDelayedPress();
866 d->handleMouseReleaseEvent(event);
867 event->accept();
868 ungrabMouse();
869 } else {
870 QDeclarativeItem::mouseReleaseEvent(event);
871 }
872}
873
874void QDeclarativeFlickable::wheelEvent(QGraphicsSceneWheelEvent *event)
875{
876 Q_D(QDeclarativeFlickable);
877 if (!d->interactive) {
878 QDeclarativeItem::wheelEvent(event);
879 } else if (yflick()) {
880 if (event->delta() > 0)
881 d->vData.velocity = qMax(event->delta() - d->vData.smoothVelocity.value(), qreal(250.0));
882 else
883 d->vData.velocity = qMin(event->delta() - d->vData.smoothVelocity.value(), qreal(-250.0));
884 d->flickingVertically = false;
885 d->flickY(d->vData.velocity);
886 if (d->flickingVertically) {
887 d->vMoved = true;
888 movementStarting();
889 }
890 event->accept();
891 } else if (xflick()) {
892 if (event->delta() > 0)
893 d->hData.velocity = qMax(event->delta() - d->hData.smoothVelocity.value(), qreal(250.0));
894 else
895 d->hData.velocity = qMin(event->delta() - d->hData.smoothVelocity.value(), qreal(-250.0));
896 d->flickingHorizontally = false;
897 d->flickX(d->hData.velocity);
898 if (d->flickingHorizontally) {
899 d->hMoved = true;
900 movementStarting();
901 }
902 event->accept();
903 } else {
904 QDeclarativeItem::wheelEvent(event);
905 }
906}
907
908void QDeclarativeFlickablePrivate::captureDelayedPress(QGraphicsSceneMouseEvent *event)
909{
910 Q_Q(QDeclarativeFlickable);
911 if (!q->scene() || pressDelay <= 0)
912 return;
913 delayedPressTarget = q->scene()->mouseGrabberItem();
914 delayedPressEvent = new QGraphicsSceneMouseEvent(event->type());
915 delayedPressEvent->setAccepted(false);
916 for (int i = 0x1; i <= 0x10; i <<= 1) {
917 if (event->buttons() & i) {
918 Qt::MouseButton button = Qt::MouseButton(i);
919 delayedPressEvent->setButtonDownPos(button, event->buttonDownPos(button));
920 delayedPressEvent->setButtonDownScenePos(button, event->buttonDownScenePos(button));
921 delayedPressEvent->setButtonDownScreenPos(button, event->buttonDownScreenPos(button));
922 }
923 }
924 delayedPressEvent->setButtons(event->buttons());
925 delayedPressEvent->setButton(event->button());
926 delayedPressEvent->setPos(event->pos());
927 delayedPressEvent->setScenePos(event->scenePos());
928 delayedPressEvent->setScreenPos(event->screenPos());
929 delayedPressEvent->setLastPos(event->lastPos());
930 delayedPressEvent->setLastScenePos(event->lastScenePos());
931 delayedPressEvent->setLastScreenPos(event->lastScreenPos());
932 delayedPressEvent->setModifiers(event->modifiers());
933 delayedPressTimer.start(pressDelay, q);
934}
935
936void QDeclarativeFlickablePrivate::clearDelayedPress()
937{
938 if (delayedPressEvent) {
939 delayedPressTimer.stop();
940 delete delayedPressEvent;
941 delayedPressEvent = 0;
942 }
943}
944
945void QDeclarativeFlickablePrivate::setRoundedViewportX(qreal x)
946{
947 contentItem->setX(qRound(x));
948}
949
950void QDeclarativeFlickablePrivate::setRoundedViewportY(qreal y)
951{
952 contentItem->setY(qRound(y));
953}
954
955void QDeclarativeFlickable::timerEvent(QTimerEvent *event)
956{
957 Q_D(QDeclarativeFlickable);
958 if (event->timerId() == d->delayedPressTimer.timerId()) {
959 d->delayedPressTimer.stop();
960 if (d->delayedPressEvent) {
961 QDeclarativeItem *grabber = scene() ? qobject_cast<QDeclarativeItem*>(scene()->mouseGrabberItem()) : 0;
962 if (!grabber || grabber != this) {
963 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
964 // so we reset the grabber
965 if (scene()->mouseGrabberItem() == d->delayedPressTarget)
966 d->delayedPressTarget->ungrabMouse();
967 //Use the event handler that will take care of finding the proper item to propagate the event
968 QApplication::sendEvent(scene(), d->delayedPressEvent);
969 }
970 delete d->delayedPressEvent;
971 d->delayedPressEvent = 0;
972 }
973 }
974}
975
976qreal QDeclarativeFlickable::minYExtent() const
977{
978 return 0.0;
979}
980
981qreal QDeclarativeFlickable::minXExtent() const
982{
983 return 0.0;
984}
985
986/* returns -ve */
987qreal QDeclarativeFlickable::maxXExtent() const
988{
989 return width() - vWidth();
990}
991/* returns -ve */
992qreal QDeclarativeFlickable::maxYExtent() const
993{
994 return height() - vHeight();
995}
996
997void QDeclarativeFlickable::viewportMoved()
998{
999 Q_D(QDeclarativeFlickable);
1000
1001 qreal prevX = d->lastFlickablePosition.x();
1002 qreal prevY = d->lastFlickablePosition.y();
1003 d->velocityTimeline.clear();
1004 if (d->pressed || d->calcVelocity) {
1005 int elapsed = QDeclarativeItemPrivate::restart(d->velocityTime);
1006 if (elapsed > 0) {
1007 qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / elapsed;
1008 qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / elapsed;
1009 d->velocityTimeline.move(d->hData.smoothVelocity, horizontalVelocity, d->reportedVelocitySmoothing);
1010 d->velocityTimeline.move(d->hData.smoothVelocity, 0, d->reportedVelocitySmoothing);
1011 d->velocityTimeline.move(d->vData.smoothVelocity, verticalVelocity, d->reportedVelocitySmoothing);
1012 d->velocityTimeline.move(d->vData.smoothVelocity, 0, d->reportedVelocitySmoothing);
1013 }
1014 } else {
1015 if (d->timeline.time() > d->vTime) {
1016 qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
1017 qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
1018 d->hData.smoothVelocity.setValue(horizontalVelocity);
1019 d->vData.smoothVelocity.setValue(verticalVelocity);
1020 }
1021 }
1022
1023 d->lastFlickablePosition = QPointF(d->hData.move.value(), d->vData.move.value());
1024
1025 d->vTime = d->timeline.time();
1026 d->updateBeginningEnd();
1027}
1028
1029void QDeclarativeFlickable::geometryChanged(const QRectF &newGeometry,
1030 const QRectF &oldGeometry)
1031{
1032 Q_D(QDeclarativeFlickable);
1033 QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
1034
1035 bool changed = false;
1036 if (newGeometry.width() != oldGeometry.width()) {
1037 if (xflick())
1038 changed = true;
1039 if (d->hData.viewSize < 0) {
1040 d->contentItem->setWidth(width());
1041 emit contentWidthChanged();
1042 }
1043 // Make sure that we're entirely in view.
1044 if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
1045 int oldDuration = d->fixupDuration;
1046 d->fixupDuration = 0;
1047 d->fixupX();
1048 d->fixupDuration = oldDuration;
1049 }
1050 }
1051 if (newGeometry.height() != oldGeometry.height()) {
1052 if (yflick())
1053 changed = true;
1054 if (d->vData.viewSize < 0) {
1055 d->contentItem->setHeight(height());
1056 emit contentHeightChanged();
1057 }
1058 // Make sure that we're entirely in view.
1059 if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
1060 int oldDuration = d->fixupDuration;
1061 d->fixupDuration = 0;
1062 d->fixupY();
1063 d->fixupDuration = oldDuration;
1064 }
1065 }
1066
1067 if (changed)
1068 d->updateBeginningEnd();
1069}
1070
1071void QDeclarativeFlickable::cancelFlick()
1072{
1073 Q_D(QDeclarativeFlickable);
1074 d->timeline.reset(d->hData.move);
1075 d->timeline.reset(d->vData.move);
1076 movementEnding();
1077}
1078
1079void QDeclarativeFlickablePrivate::data_append(QDeclarativeListProperty<QObject> *prop, QObject *o)
1080{
1081 QGraphicsObject *i = qobject_cast<QGraphicsObject *>(o);
1082 if (i) {
1083 QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(i);
1084 if (static_cast<QDeclarativeItemPrivate*>(d)->componentComplete) {
1085 i->setParentItem(static_cast<QDeclarativeFlickablePrivate*>(prop->data)->contentItem);
1086 } else {
1087 d->setParentItemHelper(static_cast<QDeclarativeFlickablePrivate*>(prop->data)->contentItem, 0, 0);
1088 }
1089 } else {
1090 o->setParent(prop->object);
1091 }
1092}
1093
1094static inline int children_count_helper(QGraphicsObject *object)
1095{
1096 QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(object);
1097 return d->children.count();
1098}
1099
1100static inline QObject *children_at_helper(QGraphicsObject *object, int index)
1101{
1102 QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(object);
1103 if (index >= 0 && index < d->children.count())
1104 return d->children.at(index)->toGraphicsObject();
1105 else
1106 return 0;
1107}
1108
1109static inline void children_clear_helper(QGraphicsObject *object)
1110{
1111 QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(object);
1112 int childCount = d->children.count();
1113 if (static_cast<QDeclarativeItemPrivate*>(d)->componentComplete) {
1114 for (int index = 0 ;index < childCount; index++) {
1115 d->children.at(0)->setParentItem(0);
1116 }
1117 } else {
1118 for (int index = 0 ;index < childCount; index++) {
1119 QGraphicsItemPrivate::get(d->children.at(0))->setParentItemHelper(0, /*newParentVariant=*/0, /*thisPointerVariant=*/0);
1120 }
1121 }
1122
1123}
1124
1125int QDeclarativeFlickablePrivate::data_count(QDeclarativeListProperty<QObject> *prop)
1126{
1127 return QDeclarativeItemPrivate::resources_count(prop) +
1128 children_count_helper(static_cast<QDeclarativeFlickablePrivate*>(prop->data)->contentItem);
1129}
1130
1131QObject *QDeclarativeFlickablePrivate::data_at(QDeclarativeListProperty<QObject> *prop, int i)
1132{
1133 int resourcesCount = QDeclarativeItemPrivate::resources_count(prop);
1134 if (i < resourcesCount)
1135 return QDeclarativeItemPrivate::resources_at(prop, i);
1136 const int j = i - resourcesCount;
1137 QGraphicsObject *contentObject = static_cast<QDeclarativeFlickablePrivate*>(prop->data)->contentItem;
1138 if (j < children_count_helper(contentObject))
1139 return children_at_helper(contentObject, j);
1140 return 0;
1141}
1142
1143void QDeclarativeFlickablePrivate::data_clear(QDeclarativeListProperty<QObject> *prop)
1144{
1145 QDeclarativeItemPrivate::resources_clear(prop);
1146 QGraphicsObject *contentObject =
1147 static_cast<QDeclarativeFlickablePrivate*>(prop->data)->contentItem;
1148 children_clear_helper(contentObject);
1149}
1150
1151QDeclarativeListProperty<QObject> QDeclarativeFlickable::flickableData()
1152{
1153 Q_D(QDeclarativeFlickable);
1154 return QDeclarativeListProperty<QObject>(this, (void *)d, QDeclarativeFlickablePrivate::data_append,
1155 QDeclarativeFlickablePrivate::data_count,
1156 QDeclarativeFlickablePrivate::data_at,
1157 QDeclarativeFlickablePrivate::data_clear
1158 );
1159}
1160
1161QDeclarativeListProperty<QGraphicsObject> QDeclarativeFlickable::flickableChildren()
1162{
1163 Q_D(QDeclarativeFlickable);
1164 return QGraphicsItemPrivate::get(d->contentItem)->childrenList();
1165}
1166
1167/*!
1168 \qmlproperty enumeration Flickable::boundsBehavior
1169 This property holds whether the surface may be dragged
1170 beyond the Fickable's boundaries, or overshoot the
1171 Flickable's boundaries when flicked.
1172
1173 This enables the feeling that the edges of the view are soft,
1174 rather than a hard physical boundary.
1175
1176 The \c boundsBehavior can be one of:
1177
1178 \list
1179 \o Flickable.StopAtBounds - the contents can not be dragged beyond the boundary
1180 of the flickable, and flicks will not overshoot.
1181 \o Flickable.DragOverBounds - the contents can be dragged beyond the boundary
1182 of the Flickable, but flicks will not overshoot.
1183 \o Flickable.DragAndOvershootBounds (default) - the contents can be dragged
1184 beyond the boundary of the Flickable, and can overshoot the
1185 boundary when flicked.
1186 \endlist
1187*/
1188QDeclarativeFlickable::BoundsBehavior QDeclarativeFlickable::boundsBehavior() const
1189{
1190 Q_D(const QDeclarativeFlickable);
1191 return d->boundsBehavior;
1192}
1193
1194void QDeclarativeFlickable::setBoundsBehavior(BoundsBehavior b)
1195{
1196 Q_D(QDeclarativeFlickable);
1197 if (b == d->boundsBehavior)
1198 return;
1199 d->boundsBehavior = b;
1200 emit boundsBehaviorChanged();
1201}
1202
1203/*!
1204 \qmlproperty real Flickable::contentWidth
1205 \qmlproperty real Flickable::contentHeight
1206
1207 The dimensions of the content (the surface controlled by Flickable).
1208 This should typically be set to the combined size of the items placed in the
1209 Flickable.
1210
1211 The following snippet shows how these properties are used to display
1212 an image that is larger than the Flickable item itself:
1213
1214 \snippet doc/src/snippets/declarative/flickable.qml document
1215
1216 In some cases, the the content dimensions can be automatically set
1217 using the \l {Item::childrenRect.width}{childrenRect.width}
1218 and \l {Item::childrenRect.height}{childrenRect.height} properties.
1219*/
1220qreal QDeclarativeFlickable::contentWidth() const
1221{
1222 Q_D(const QDeclarativeFlickable);
1223 return d->hData.viewSize;
1224}
1225
1226void QDeclarativeFlickable::setContentWidth(qreal w)
1227{
1228 Q_D(QDeclarativeFlickable);
1229 if (d->hData.viewSize == w)
1230 return;
1231 d->hData.viewSize = w;
1232 if (w < 0)
1233 d->contentItem->setWidth(width());
1234 else
1235 d->contentItem->setWidth(w);
1236 // Make sure that we're entirely in view.
1237 if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
1238 int oldDuration = d->fixupDuration;
1239 d->fixupDuration = 0;
1240 d->fixupX();
1241 d->fixupDuration = oldDuration;
1242 }
1243 emit contentWidthChanged();
1244 d->updateBeginningEnd();
1245}
1246
1247qreal QDeclarativeFlickable::contentHeight() const
1248{
1249 Q_D(const QDeclarativeFlickable);
1250 return d->vData.viewSize;
1251}
1252
1253void QDeclarativeFlickable::setContentHeight(qreal h)
1254{
1255 Q_D(QDeclarativeFlickable);
1256 if (d->vData.viewSize == h)
1257 return;
1258 d->vData.viewSize = h;
1259 if (h < 0)
1260 d->contentItem->setHeight(height());
1261 else
1262 d->contentItem->setHeight(h);
1263 // Make sure that we're entirely in view.
1264 if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
1265 int oldDuration = d->fixupDuration;
1266 d->fixupDuration = 0;
1267 d->fixupY();
1268 d->fixupDuration = oldDuration;
1269 }
1270 emit contentHeightChanged();
1271 d->updateBeginningEnd();
1272}
1273
1274qreal QDeclarativeFlickable::vWidth() const
1275{
1276 Q_D(const QDeclarativeFlickable);
1277 if (d->hData.viewSize < 0)
1278 return width();
1279 else
1280 return d->hData.viewSize;
1281}
1282
1283qreal QDeclarativeFlickable::vHeight() const
1284{
1285 Q_D(const QDeclarativeFlickable);
1286 if (d->vData.viewSize < 0)
1287 return height();
1288 else
1289 return d->vData.viewSize;
1290}
1291
1292bool QDeclarativeFlickable::xflick() const
1293{
1294 Q_D(const QDeclarativeFlickable);
1295 if (d->flickableDirection == QDeclarativeFlickable::AutoFlickDirection)
1296 return vWidth() != width();
1297 return d->flickableDirection & QDeclarativeFlickable::HorizontalFlick;
1298}
1299
1300bool QDeclarativeFlickable::yflick() const
1301{
1302 Q_D(const QDeclarativeFlickable);
1303 if (d->flickableDirection == QDeclarativeFlickable::AutoFlickDirection)
1304 return vHeight() != height();
1305 return d->flickableDirection & QDeclarativeFlickable::VerticalFlick;
1306}
1307
1308bool QDeclarativeFlickable::sendMouseEvent(QGraphicsSceneMouseEvent *event)
1309{
1310 Q_D(QDeclarativeFlickable);
1311 QGraphicsSceneMouseEvent mouseEvent(event->type());
1312 QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect();
1313
1314 QGraphicsScene *s = scene();
1315 QDeclarativeItem *grabber = s ? qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem()) : 0;
1316 bool stealThisEvent = d->stealMouse;
1317 if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) {
1318 mouseEvent.setAccepted(false);
1319 for (int i = 0x1; i <= 0x10; i <<= 1) {
1320 if (event->buttons() & i) {
1321 Qt::MouseButton button = Qt::MouseButton(i);
1322 mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
1323 }
1324 }
1325 mouseEvent.setScenePos(event->scenePos());
1326 mouseEvent.setLastScenePos(event->lastScenePos());
1327 mouseEvent.setPos(mapFromScene(event->scenePos()));
1328 mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
1329
1330 switch(mouseEvent.type()) {
1331 case QEvent::GraphicsSceneMouseMove:
1332 d->handleMouseMoveEvent(&mouseEvent);
1333 break;
1334 case QEvent::GraphicsSceneMousePress:
1335 if (d->delayedPressEvent)
1336 return false;
1337
1338 d->handleMousePressEvent(&mouseEvent);
1339 d->captureDelayedPress(event);
1340 stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
1341 break;
1342 case QEvent::GraphicsSceneMouseRelease:
1343 if (d->delayedPressEvent) {
1344 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1345 // so we reset the grabber
1346 if (s->mouseGrabberItem() == d->delayedPressTarget)
1347 d->delayedPressTarget->ungrabMouse();
1348 //Use the event handler that will take care of finding the proper item to propagate the event
1349 QApplication::sendEvent(scene(), d->delayedPressEvent);
1350 d->clearDelayedPress();
1351 // We send the release
1352 scene()->sendEvent(s->mouseGrabberItem(), event);
1353 // And the event has been consumed
1354 return true;
1355 }
1356 d->handleMouseReleaseEvent(&mouseEvent);
1357 break;
1358 default:
1359 break;
1360 }
1361 grabber = qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem());
1362 if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) {
1363 d->clearDelayedPress();
1364 grabMouse();
1365 }
1366
1367 return stealThisEvent || d->delayedPressEvent;
1368 } else if (d->lastPosTime.isValid()) {
1369 d->lastPosTime.invalidate();
1370 }
1371 if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) {
1372 d->clearDelayedPress();
1373 d->stealMouse = false;
1374 d->pressed = false;
1375 }
1376 return false;
1377}
1378
1379bool QDeclarativeFlickable::sceneEventFilter(QGraphicsItem *i, QEvent *e)
1380{
1381 Q_D(QDeclarativeFlickable);
1382 if (!isVisible() || !d->interactive)
1383 return QDeclarativeItem::sceneEventFilter(i, e);
1384 switch (e->type()) {
1385 case QEvent::GraphicsSceneMousePress:
1386 case QEvent::GraphicsSceneMouseMove:
1387 case QEvent::GraphicsSceneMouseRelease:
1388 return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
1389 default:
1390 break;
1391 }
1392
1393 return QDeclarativeItem::sceneEventFilter(i, e);
1394}
1395
1396/*!
1397 \qmlproperty real Flickable::maximumFlickVelocity
1398 This property holds the maximum velocity that the user can flick the view in pixels/second.
1399
1400 The default is 2000 pixels/s
1401*/
1402qreal QDeclarativeFlickable::maximumFlickVelocity() const
1403{
1404 Q_D(const QDeclarativeFlickable);
1405 return d->maxVelocity;
1406}
1407
1408void QDeclarativeFlickable::setMaximumFlickVelocity(qreal v)
1409{
1410 Q_D(QDeclarativeFlickable);
1411 if (v == d->maxVelocity)
1412 return;
1413 d->maxVelocity = v;
1414 emit maximumFlickVelocityChanged();
1415}
1416
1417/*!
1418 \qmlproperty real Flickable::flickDeceleration
1419 This property holds the rate at which a flick will decelerate.
1420
1421 The default is 500.
1422*/
1423qreal QDeclarativeFlickable::flickDeceleration() const
1424{
1425 Q_D(const QDeclarativeFlickable);
1426 return d->deceleration;
1427}
1428
1429void QDeclarativeFlickable::setFlickDeceleration(qreal deceleration)
1430{
1431 Q_D(QDeclarativeFlickable);
1432 if (deceleration == d->deceleration)
1433 return;
1434 d->deceleration = deceleration;
1435 emit flickDecelerationChanged();
1436}
1437
1438bool QDeclarativeFlickable::isFlicking() const
1439{
1440 Q_D(const QDeclarativeFlickable);
1441 return d->flickingHorizontally || d->flickingVertically;
1442}
1443
1444/*!
1445 \qmlproperty bool Flickable::flicking
1446 \qmlproperty bool Flickable::flickingHorizontally
1447 \qmlproperty bool Flickable::flickingVertically
1448
1449 These properties describe whether the view is currently moving horizontally,
1450 vertically or in either direction, due to the user flicking the view.
1451*/
1452bool QDeclarativeFlickable::isFlickingHorizontally() const
1453{
1454 Q_D(const QDeclarativeFlickable);
1455 return d->flickingHorizontally;
1456}
1457
1458bool QDeclarativeFlickable::isFlickingVertically() const
1459{
1460 Q_D(const QDeclarativeFlickable);
1461 return d->flickingVertically;
1462}
1463
1464/*!
1465 \qmlproperty int Flickable::pressDelay
1466
1467 This property holds the time to delay (ms) delivering a press to
1468 children of the Flickable. This can be useful where reacting
1469 to a press before a flicking action has undesirable effects.
1470
1471 If the flickable is dragged/flicked before the delay times out
1472 the press event will not be delivered. If the button is released
1473 within the timeout, both the press and release will be delivered.
1474*/
1475int QDeclarativeFlickable::pressDelay() const
1476{
1477 Q_D(const QDeclarativeFlickable);
1478 return d->pressDelay;
1479}
1480
1481void QDeclarativeFlickable::setPressDelay(int delay)
1482{
1483 Q_D(QDeclarativeFlickable);
1484 if (d->pressDelay == delay)
1485 return;
1486 d->pressDelay = delay;
1487 emit pressDelayChanged();
1488}
1489
1490
1491bool QDeclarativeFlickable::isMoving() const
1492{
1493 Q_D(const QDeclarativeFlickable);
1494 return d->movingHorizontally || d->movingVertically;
1495}
1496
1497/*!
1498 \qmlproperty bool Flickable::moving
1499 \qmlproperty bool Flickable::movingHorizontally
1500 \qmlproperty bool Flickable::movingVertically
1501
1502 These properties describe whether the view is currently moving horizontally,
1503 vertically or in either direction, due to the user either dragging or
1504 flicking the view.
1505*/
1506bool QDeclarativeFlickable::isMovingHorizontally() const
1507{
1508 Q_D(const QDeclarativeFlickable);
1509 return d->movingHorizontally;
1510}
1511
1512bool QDeclarativeFlickable::isMovingVertically() const
1513{
1514 Q_D(const QDeclarativeFlickable);
1515 return d->movingVertically;
1516}
1517
1518void QDeclarativeFlickable::movementStarting()
1519{
1520 Q_D(QDeclarativeFlickable);
1521 if (d->hMoved && !d->movingHorizontally) {
1522 d->movingHorizontally = true;
1523 emit movingChanged();
1524 emit movingHorizontallyChanged();
1525 if (!d->movingVertically)
1526 emit movementStarted();
1527 }
1528 else if (d->vMoved && !d->movingVertically) {
1529 d->movingVertically = true;
1530 emit movingChanged();
1531 emit movingVerticallyChanged();
1532 if (!d->movingHorizontally)
1533 emit movementStarted();
1534 }
1535}
1536
1537void QDeclarativeFlickable::movementEnding()
1538{
1539 Q_D(QDeclarativeFlickable);
1540 movementXEnding();
1541 movementYEnding();
1542 d->hData.smoothVelocity.setValue(0);
1543 d->vData.smoothVelocity.setValue(0);
1544}
1545
1546void QDeclarativeFlickable::movementXEnding()
1547{
1548 Q_D(QDeclarativeFlickable);
1549 if (d->flickingHorizontally) {
1550 d->flickingHorizontally = false;
1551 emit flickingChanged();
1552 emit flickingHorizontallyChanged();
1553 if (!d->flickingVertically)
1554 emit flickEnded();
1555 }
1556 if (!d->pressed && !d->stealMouse) {
1557 if (d->movingHorizontally) {
1558 d->movingHorizontally = false;
1559 d->hMoved = false;
1560 emit movingChanged();
1561 emit movingHorizontallyChanged();
1562 if (!d->movingVertically)
1563 emit movementEnded();
1564 }
1565 }
1566}
1567
1568void QDeclarativeFlickable::movementYEnding()
1569{
1570 Q_D(QDeclarativeFlickable);
1571 if (d->flickingVertically) {
1572 d->flickingVertically = false;
1573 emit flickingChanged();
1574 emit flickingVerticallyChanged();
1575 if (!d->flickingHorizontally)
1576 emit flickEnded();
1577 }
1578 if (!d->pressed && !d->stealMouse) {
1579 if (d->movingVertically) {
1580 d->movingVertically = false;
1581 d->vMoved = false;
1582 emit movingChanged();
1583 emit movingVerticallyChanged();
1584 if (!d->movingHorizontally)
1585 emit movementEnded();
1586 }
1587 }
1588}
1589
1590void QDeclarativeFlickablePrivate::updateVelocity()
1591{
1592 Q_Q(QDeclarativeFlickable);
1593 emit q->horizontalVelocityChanged();
1594 emit q->verticalVelocityChanged();
1595}
1596
1597QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.