Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickswipedelegate.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
10
11#include <QtGui/qstylehints.h>
12#include <QtGui/private/qguiapplication_p.h>
13#include <QtGui/private/qeventpoint_p.h>
14#include <QtGui/qpa/qplatformtheme.h>
15#include <QtQml/qqmlinfo.h>
16#include <QtQuick/private/qquickanimation_p.h>
17#include <QtQuick/private/qquicktransition_p.h>
18#include <QtQuick/private/qquicktransitionmanager_p_p.h>
19
20#include <algorithm>
21
22QT_BEGIN_NAMESPACE
23
24/*!
25 \qmltype SwipeDelegate
26 \inherits ItemDelegate
27//! \nativetype QQuickSwipeDelegate
28 \inqmlmodule QtQuick.Controls
29 \since 5.7
30 \ingroup qtquickcontrols-delegates
31 \brief Swipable item delegate.
32
33 SwipeDelegate presents a view item that can be swiped left or right to
34 expose more options or information. It is used as a delegate in views such
35 as \l ListView.
36
37 In the following example, SwipeDelegate is used in a \l ListView to allow
38 items to be removed from it by swiping to the left:
39
40 \snippet qtquickcontrols-swipedelegate.qml 1
41
42 SwipeDelegate inherits its API from \l ItemDelegate, which is inherited
43 from AbstractButton. For instance, you can set \l {AbstractButton::text}{text},
44 and react to \l {AbstractButton::clicked}{clicks} using the AbstractButton
45 API.
46
47 Information regarding the progress of a swipe, as well as the components
48 that should be shown upon swiping, are both available through the
49 \l {SwipeDelegate::}{swipe} grouped property object. For example,
50 \c swipe.position holds the position of the
51 swipe within the range \c -1.0 to \c 1.0. The \c swipe.left
52 property determines which item will be displayed when the control is swiped
53 to the right, and vice versa for \c swipe.right. The positioning of these
54 components is left to applications to decide. For example, without specifying
55 any position for \c swipe.left or \c swipe.right, the following will
56 occur:
57
58 \image qtquickcontrols-swipedelegate.gif
59
60 If \c swipe.left and \c swipe.right are anchored to the left and
61 right of the \l {Control::}{background} item (respectively), they'll behave like this:
62
63 \image qtquickcontrols-swipedelegate-leading-trailing.gif
64
65 When using \c swipe.left and \c swipe.right, the control cannot be
66 swiped past the left and right edges. To achieve this type of "wrapping"
67 behavior, set \c swipe.behind instead. This will result in the same
68 item being shown regardless of which direction the control is swiped. For
69 example, in the image below, we set \c swipe.behind and then swipe the
70 control repeatedly in both directions:
71
72 \image qtquickcontrols-swipedelegate-behind.gif
73
74 \sa {Customizing SwipeDelegate}, {Delegate Controls}, {Qt Quick Controls 2 - Gallery}{Gallery Example}
75*/
76
77namespace {
78 typedef QQuickSwipeDelegateAttached Attached;
79
80 Attached *attachedObject(QQuickItem *item) {
81 return qobject_cast<Attached*>(qmlAttachedPropertiesObject<QQuickSwipeDelegate>(item, false));
82 }
83
88}
89
91{
92public:
93 QQuickSwipeTransitionManager(QQuickSwipe *swipe);
94
95 void transition(QQuickTransition *transition, qreal position);
96
97protected:
99
100private:
101 QQuickSwipe *m_swipe = nullptr;
102};
103
105{
106 Q_DECLARE_PUBLIC(QQuickSwipe)
107
108public:
110
112
113 QQuickItem *createDelegateItem(QQmlComponent *component);
116 void reposition(PositionAnimation animationPolicy);
123
126
127 bool hasDelegates() const;
128
129 bool isTransitioning() const;
130 void beginTransition(qreal position);
132
134 // Same range as position, but is set before press events so that we can
135 // keep track of which direction the user must swipe when using left and right delegates.
138 // A "less strict" version of complete that is true if complete was true
139 // before the last press event.
140 bool wasComplete = false;
141 bool complete = false;
142 bool enabled = true;
143 bool waitForTransition = false;
144 QQuickVelocityCalculator velocityCalculator;
145 QQmlComponent *left = nullptr;
153};
154
156 : m_swipe(swipe)
157{
158}
159
160void QQuickSwipeTransitionManager::transition(QQuickTransition *transition, qreal position)
161{
162 qmlExecuteDeferred(transition);
163
164 QQmlProperty defaultTarget(m_swipe, QLatin1String("position"));
165 QQmlListProperty<QQuickAbstractAnimation> animations = transition->animations();
166 const int count = animations.count(&animations);
167 for (int i = 0; i < count; ++i) {
168 QQuickAbstractAnimation *anim = animations.at(&animations, i);
169 anim->setDefaultTarget(defaultTarget);
170 }
171
172 QList<QQuickStateAction> actions;
173 actions << QQuickStateAction(m_swipe, QLatin1String("position"), position);
174 QQuickTransitionManager::transition(actions, transition, m_swipe);
175}
176
178{
179 QQuickSwipePrivate::get(m_swipe)->finishTransition();
180}
181
182QQuickSwipePrivate *QQuickSwipePrivate::get(QQuickSwipe *swipe)
183{
184 return swipe->d_func();
185}
186
188{
189 // If we don't use the correct context, it won't be possible to refer to
190 // the control's id from within the delegates.
191 QQmlContext *context = component->creationContext();
192 // The component might not have been created in QML, in which case
193 // the creation context will be null and we have to create it ourselves.
194 if (!context)
195 context = qmlContext(control);
196 QQuickItem *item = qobject_cast<QQuickItem*>(component->beginCreate(context));
197 if (item) {
198 item->setParentItem(control);
199 component->completeCreate();
200 QJSEngine::setObjectOwnership(item, QJSEngine::JavaScriptOwnership);
201 }
202 return item;
203}
204
206{
207 if (qFuzzyIsNull(position))
208 return nullptr;
209
210 if (behind) {
212 return behindItem;
213 }
214
215 if (right && position < 0.0) {
217 return rightItem;
218 }
219
220 if (left && position > 0.0) {
222 return leftItem;
223 }
224
225 return nullptr;
226}
227
229{
230 if (qFuzzyIsNull(distance))
231 return nullptr;
232
233 if (behind) {
235 return behindItem;
236 }
237
238 // a) If the position before the press was 0.0, we know that *any* movement
239 // whose distance is negative will result in the right item being shown and
240 // vice versa.
241 // b) Once the control has been exposed (that is, swiped to the left or right,
242 // and hence the position is either -1.0 or 1.0), we must use the width of the
243 // relevant item to determine if the distance is larger than that item,
244 // in order to know whether or not to display it.
245 // c) If the control has been exposed, and the swipe is larger than the width
246 // of the relevant item from which the swipe started from, we must show the
247 // item on the other side (if any).
248
249 if (right) {
250 if ((distance < 0.0 && positionBeforePress == 0.0) /* a) */
251 || (rightItem && positionBeforePress == -1.0 && distance < rightItem->width()) /* b) */
252 || (leftItem && positionBeforePress == 1.0 && qAbs(distance) > leftItem->width())) /* c) */ {
254 return rightItem;
255 }
256 }
257
258 if (left) {
259 if ((distance > 0.0 && positionBeforePress == 0.0) /* a) */
260 || (leftItem && positionBeforePress == 1.0 && qAbs(distance) < leftItem->width()) /* b) */
261 || (rightItem && positionBeforePress == -1.0 && qAbs(distance) > rightItem->width())) /* c) */ {
263 return leftItem;
264 }
265 }
266
267 return nullptr;
268}
269
271{
272 QQuickItem *relevantItem = showRelevantItemForPosition(position);
273 const qreal relevantWidth = relevantItem ? relevantItem->width() : 0.0;
274 const qreal contentItemX = position * relevantWidth + control->leftPadding();
275
276 // "Behavior on x" relies on the property system to know when it should update,
277 // so we can prevent it from animating by setting the x position directly.
278 if (animationPolicy == AnimatePosition) {
279 if (QQuickItem *contentItem = control->contentItem())
280 contentItem->setProperty("x", contentItemX);
281 if (QQuickItem *background = control->background())
282 background->setProperty("x", position * relevantWidth);
283 } else {
284 if (QQuickItem *contentItem = control->contentItem())
285 contentItem->setX(contentItemX);
286 if (QQuickItem *background = control->background())
287 background->setX(position * relevantWidth);
288 }
289}
290
292{
293 if (!leftItem) {
294 Q_Q(QQuickSwipe);
295 q->setLeftItem(createDelegateItem(left));
296 if (!leftItem)
297 qmlWarning(control) << "Failed to create left item:" << left->errors();
298 }
299}
300
302{
303 if (!behindItem) {
304 Q_Q(QQuickSwipe);
305 q->setBehindItem(createDelegateItem(behind));
306 if (!behindItem)
307 qmlWarning(control) << "Failed to create behind item:" << behind->errors();
308 }
309}
310
312{
313 if (!rightItem) {
314 Q_Q(QQuickSwipe);
315 q->setRightItem(createDelegateItem(right));
316 if (!rightItem)
317 qmlWarning(control) << "Failed to create right item:" << right->errors();
318 }
319}
320
322{
324
325 if (leftItem)
326 leftItem->setVisible(true);
327
328 if (rightItem)
329 rightItem->setVisible(false);
330}
331
333{
335
336 if (behindItem)
337 behindItem->setVisible(true);
338}
339
341{
343
344 // This item may have already existed but was hidden.
345 if (rightItem)
346 rightItem->setVisible(true);
347
348 // The left item isn't visible when the right item is visible, so save rendering effort by hiding it.
349 if (leftItem)
350 leftItem->setVisible(false);
351}
352
354{
355 qmlWarning(control) << "cannot set both behind and left/right properties";
356}
357
359{
360 qmlWarning(control) << "left/right/behind properties may only be set when swipe.position is 0";
361}
362
364{
365 return left || right || behind;
366}
367
369{
370 return transitionManager && transitionManager->isRunning();
371}
372
373void QQuickSwipePrivate::beginTransition(qreal newPosition)
374{
376 return;
377 Q_Q(QQuickSwipe);
378 if (!transition) {
379 q->setPosition(newPosition);
381 return;
382 }
383
384 if (!transitionManager)
385 transitionManager.reset(new QQuickSwipeTransitionManager(q));
386
387 transitionManager->transition(transition, newPosition);
388}
389
391{
392 Q_Q(QQuickSwipe);
393 waitForTransition = false;
394 q->setComplete(qFuzzyCompare(qAbs(position), qreal(1.0)));
395 if (complete) {
396 emit q->opened();
397 } else {
398 if (qFuzzyIsNull(position))
399 wasComplete = false;
400 emit q->closed();
401 }
402}
403
404QQuickSwipe::QQuickSwipe(QQuickSwipeDelegate *control)
405 : QObject(*(new QQuickSwipePrivate(control)))
406{
407}
408
409QQmlComponent *QQuickSwipe::left() const
410{
411 Q_D(const QQuickSwipe);
412 return d->left;
413}
414
415void QQuickSwipe::setLeft(QQmlComponent *left)
416{
417 Q_D(QQuickSwipe);
418 if (left == d->left)
419 return;
420
421 if (d->behind) {
422 d->warnAboutMixingDelegates();
423 return;
424 }
425
426 if (!qFuzzyIsNull(d->position)) {
427 d->warnAboutSettingDelegatesWhileVisible();
428 return;
429 }
430
431 d->left = left;
432
433 if (!d->left) {
434 delete d->leftItem;
435 d->leftItem = nullptr;
436 }
437
438 d->control->setFiltersChildMouseEvents(d->hasDelegates());
439
440 emit leftChanged();
441}
442
443QQmlComponent *QQuickSwipe::behind() const
444{
445 Q_D(const QQuickSwipe);
446 return d->behind;
447}
448
449void QQuickSwipe::setBehind(QQmlComponent *behind)
450{
451 Q_D(QQuickSwipe);
452 if (behind == d->behind)
453 return;
454
455 if (d->left || d->right) {
456 d->warnAboutMixingDelegates();
457 return;
458 }
459
460 if (!qFuzzyIsNull(d->position)) {
461 d->warnAboutSettingDelegatesWhileVisible();
462 return;
463 }
464
465 d->behind = behind;
466
467 if (!d->behind) {
468 delete d->behindItem;
469 d->behindItem = nullptr;
470 }
471
472 d->control->setFiltersChildMouseEvents(d->hasDelegates());
473
474 emit behindChanged();
475}
476
477QQmlComponent *QQuickSwipe::right() const
478{
479 Q_D(const QQuickSwipe);
480 return d->right;
481}
482
483void QQuickSwipe::setRight(QQmlComponent *right)
484{
485 Q_D(QQuickSwipe);
486 if (right == d->right)
487 return;
488
489 if (d->behind) {
490 d->warnAboutMixingDelegates();
491 return;
492 }
493
494 if (!qFuzzyIsNull(d->position)) {
495 d->warnAboutSettingDelegatesWhileVisible();
496 return;
497 }
498
499 d->right = right;
500
501 if (!d->right) {
502 delete d->rightItem;
503 d->rightItem = nullptr;
504 }
505
506 d->control->setFiltersChildMouseEvents(d->hasDelegates());
507
508 emit rightChanged();
509}
510
511QQuickItem *QQuickSwipe::leftItem() const
512{
513 Q_D(const QQuickSwipe);
514 return d->leftItem;
515}
516
517void QQuickSwipe::setLeftItem(QQuickItem *item)
518{
519 Q_D(QQuickSwipe);
520 if (item == d->leftItem)
521 return;
522
523 delete d->leftItem;
524 d->leftItem = item;
525
526 if (d->leftItem) {
527 d->leftItem->setParentItem(d->control);
528
529 if (qFuzzyIsNull(d->leftItem->z()))
530 d->leftItem->setZ(-2);
531 }
532
533 emit leftItemChanged();
534}
535
536QQuickItem *QQuickSwipe::behindItem() const
537{
538 Q_D(const QQuickSwipe);
539 return d->behindItem;
540}
541
542void QQuickSwipe::setBehindItem(QQuickItem *item)
543{
544 Q_D(QQuickSwipe);
545 if (item == d->behindItem)
546 return;
547
548 delete d->behindItem;
549 d->behindItem = item;
550
551 if (d->behindItem) {
552 d->behindItem->setParentItem(d->control);
553
554 if (qFuzzyIsNull(d->behindItem->z()))
555 d->behindItem->setZ(-2);
556 }
557
558 emit behindItemChanged();
559}
560
561QQuickItem *QQuickSwipe::rightItem() const
562{
563 Q_D(const QQuickSwipe);
564 return d->rightItem;
565}
566
567void QQuickSwipe::setRightItem(QQuickItem *item)
568{
569 Q_D(QQuickSwipe);
570 if (item == d->rightItem)
571 return;
572
573 delete d->rightItem;
574 d->rightItem = item;
575
576 if (d->rightItem) {
577 d->rightItem->setParentItem(d->control);
578
579 if (qFuzzyIsNull(d->rightItem->z()))
580 d->rightItem->setZ(-2);
581 }
582
583 emit rightItemChanged();
584}
585
586qreal QQuickSwipe::position() const
587{
588 Q_D(const QQuickSwipe);
589 return d->position;
590}
591
592void QQuickSwipe::setPosition(qreal position)
593{
594 Q_D(QQuickSwipe);
595 const qreal adjustedPosition = std::clamp(position, qreal(-1.0), qreal(1.0));
596 if (adjustedPosition == d->position)
597 return;
598
599 d->position = adjustedPosition;
600 d->reposition(AnimatePosition);
601 emit positionChanged();
602}
603
604bool QQuickSwipe::isComplete() const
605{
606 Q_D(const QQuickSwipe);
607 return d->complete;
608}
609
610void QQuickSwipe::setComplete(bool complete)
611{
612 Q_D(QQuickSwipe);
613 if (complete == d->complete)
614 return;
615
616 d->complete = complete;
617 emit completeChanged();
618 if (d->complete)
619 emit completed();
620}
621
622bool QQuickSwipe::isEnabled() const
623{
624 Q_D(const QQuickSwipe);
625 return d->enabled;
626}
627
628void QQuickSwipe::setEnabled(bool enabled)
629{
630 Q_D(QQuickSwipe);
631 if (enabled == d->enabled)
632 return;
633
634 d->enabled = enabled;
635 emit enabledChanged();
636}
637
638QQuickTransition *QQuickSwipe::transition() const
639{
640 Q_D(const QQuickSwipe);
641 return d->transition;
642}
643
644void QQuickSwipe::setTransition(QQuickTransition *transition)
645{
646 Q_D(QQuickSwipe);
647 if (transition == d->transition)
648 return;
649
650 d->transition = transition;
651 emit transitionChanged();
652}
653
654void QQuickSwipe::open(QQuickSwipeDelegate::Side side)
655{
656 Q_D(QQuickSwipe);
657 if (qFuzzyCompare(qAbs(d->position), qreal(1.0)))
658 return;
659
660 if ((side != QQuickSwipeDelegate::Left && side != QQuickSwipeDelegate::Right)
661 || (!d->left && !d->behind && side == QQuickSwipeDelegate::Left)
662 || (!d->right && !d->behind && side == QQuickSwipeDelegate::Right))
663 return;
664
665 d->beginTransition(side);
666 d->wasComplete = true;
667 d->velocityCalculator.reset();
668 d->positionBeforePress = d->position;
669}
670
671void QQuickSwipe::close()
672{
673 Q_D(QQuickSwipe);
674 if (qFuzzyIsNull(d->position))
675 return;
676
677 if (d->control->isPressed()) {
678 // We don't support closing when we're pressed; release() or clicked() should be used instead.
679 return;
680 }
681
682 d->beginTransition(0.0);
683 d->waitForTransition = true;
684 d->wasComplete = false;
685 d->positionBeforePress = 0.0;
686 d->velocityCalculator.reset();
687}
688
689QQuickSwipeDelegatePrivate::QQuickSwipeDelegatePrivate(QQuickSwipeDelegate *control)
690 : swipe(control)
691{
692}
693
695{
696 if (!background)
697 return;
698
699 resizingBackground = true;
700
701 QQuickItemPrivate *p = QQuickItemPrivate::get(background);
702 const bool extraAllocated = extra.isAllocated();
703 // Don't check for or set the x here since it will just be overwritten by reposition().
704 if (((!p->widthValid() || !extraAllocated || !extra->hasBackgroundWidth))
705 || (extraAllocated && (extra->hasLeftInset || extra->hasRightInset))) {
706 background->setWidth(width - getLeftInset() - getRightInset());
707 }
708 if (((!p->heightValid() || !extraAllocated || !extra->hasBackgroundHeight) && qFuzzyIsNull(background->y()))
709 || (extraAllocated && (extra->hasTopInset || extra->hasBottomInset))) {
710 background->setY(getTopInset());
711 background->setHeight(height - getTopInset() - getBottomInset());
712 }
713
714 resizingBackground = false;
715}
716
717bool QQuickSwipeDelegatePrivate::handleMousePressEvent(QQuickItem *item, QMouseEvent *event)
718{
719 Q_Q(QQuickSwipeDelegate);
720 const auto posInItem = item->mapToItem(q, event->position().toPoint());
721 QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&swipe);
722 // If the position is 0, we want to handle events ourselves - we don't want child items to steal them.
723 // This code will only get called when a child item has been created;
724 // events will go through the regular channels (mousePressEvent()) until then.
725 if (qFuzzyIsNull(swipePrivate->position)) {
726 q->mousePressEvent(event);
727 // The press point could be incorrect if the press happened over a child item,
728 // so we correct it after calling the base class' mousePressEvent(), rather
729 // than having to duplicate its code just so we can set the pressPoint.
730 setPressPoint(posInItem);
731 return true;
732 }
733
734 // If the delegate is swiped open, send the event to the exposed item,
735 // in case it's an interactive child (like a Button).
736 if (swipePrivate->complete)
737 forwardMouseEvent(event, item, posInItem);
738
739 // The position is non-zero, this press could be either for a delegate or the control itself
740 // (the control can be clicked to e.g. close the swipe). Either way, we must begin measuring
741 // mouse movement in case it turns into a swipe, in which case we grab the mouse.
742 swipePrivate->positionBeforePress = swipePrivate->position;
743 swipePrivate->velocityCalculator.startMeasuring(event->position().toPoint(), event->timestamp());
744 setPressPoint(item->mapToItem(q, event->position().toPoint()));
745
746 // When a delegate or any of its children uses the attached properties and signals,
747 // it declares that it wants mouse events.
748 const bool delivered = attachedObjectsSetPressed(item, event->scenePosition(), true);
749 if (delivered)
750 event->accept();
751 return delivered;
752}
753
754bool QQuickSwipeDelegatePrivate::handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event)
755{
756 Q_Q(QQuickSwipeDelegate);
757
758 if (holdTimer > 0) {
759 if (QLineF(pressPoint, event->position()).length() > QGuiApplication::styleHints()->startDragDistance())
760 stopPressAndHold();
761 }
762
763 // The delegate can still be pressed when swipe.enabled is false,
764 // but the mouse moving shouldn't have any effect on swipe.position.
765 QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&swipe);
766 if (!swipePrivate->enabled)
767 return false;
768
769 // Protect against division by zero.
770 if (width == 0)
771 return false;
772
773 // Don't bother reacting to events if we don't have any delegates.
774 if (!swipePrivate->left && !swipePrivate->right && !swipePrivate->behind)
775 return false;
776
777 if (item != q && swipePrivate->complete) {
778 // If the delegate is swiped open, send the event to the exposed item,
779 // in case it's an interactive child (like a Button).
780 const auto posInItem = item->mapToItem(q, event->position().toPoint());
781 forwardMouseEvent(event, item, posInItem);
782 }
783
784 // Don't handle move events for the control if it wasn't pressed.
785 if (item == q && !pressed)
786 return false;
787
788 static constexpr QGuiApplicationPrivate::QLastCursorPosition uninitializedCursorPosition;
789 const qreal distance = (event->globalPosition() == uninitializedCursorPosition ? 0 :
790 (item->mapFromGlobal(event->globalPosition()) -
791 item->mapFromGlobal(event->points().first().globalPressPosition())).x());
792 if (!q->keepMouseGrab()) {
793 // We used to use the custom threshold that QQuickDrawerPrivate::grabMouse used,
794 // but since it's larger than what Flickable uses, it results in Flickable
795 // stealing events from us (QTBUG-50045), so now we use the default.
796 const bool overThreshold = QQuickDeliveryAgentPrivate::dragOverThreshold(distance,
797 Qt::XAxis, event);
798 if (window && overThreshold) {
799 QQuickItem *grabber = q->window()->mouseGrabberItem();
800 if (!grabber || !grabber->keepMouseGrab()) {
801 q->grabMouse();
802 q->setKeepMouseGrab(true);
803 q->setPressed(true);
804 swipe.setComplete(false);
805
806 attachedObjectsSetPressed(item, event->scenePosition(), false, true);
807 }
808 }
809 }
810
811 if (q->keepMouseGrab()) {
812 // Ensure we don't try to calculate a position when the user tried to drag
813 // to the left when the left item is already exposed, and vice versa.
814 // The code below assumes that the drag is valid, so if we don't have this check,
815 // the wrong items are visible and the swiping wraps.
816 if (swipePrivate->behind
817 || ((swipePrivate->left || swipePrivate->right)
818 && (qFuzzyIsNull(swipePrivate->positionBeforePress)
819 || (swipePrivate->positionBeforePress == -1.0 && distance >= 0.0)
820 || (swipePrivate->positionBeforePress == 1.0 && distance <= 0.0)))) {
821
822 // We must instantiate the items here so that we can calculate the
823 // position against the width of the relevant item.
824 QQuickItem *relevantItem = swipePrivate->createRelevantItemForDistance(distance);
825 // If there isn't any relevant item, the user may have swiped back to the 0 position,
826 // or they swiped back to a position that is equal to positionBeforePress.
827 const qreal normalizedDistance = relevantItem ? distance / relevantItem->width() : 0.0;
828 qreal position = 0;
829
830 // If the control was exposed before the drag begun, the distance should be inverted.
831 // For example, if the control had been swiped to the right, the position would be 1.0.
832 // If the control was then swiped to the left by a distance of -20 pixels, the normalized
833 // distance might be -0.2, for example, which cannot be used as the position; the swipe
834 // started from the right, so we account for that by adding the position.
835 if (qFuzzyIsNull(normalizedDistance)) {
836 // There are two cases when the normalizedDistance can be 0,
837 // and we must distinguish between them:
838 //
839 // a) The swipe returns to the position that it was at before the press event.
840 // In this case, the distance will be 0.
841 // There would have been many position changes in the meantime, so we can't just
842 // ignore the move event; we have to set position to what it was before the press.
843 //
844 // b) If the position was at, 1.0, for example, and the control was then swiped
845 // to the left by the exact width of the left item, there won't be any relevant item
846 // (because the swipe's position would be at 0.0). In turn, the normalizedDistance
847 // would be 0 (because of the lack of a relevant item), but the distance will be non-zero.
848 position = qFuzzyIsNull(distance) ? swipePrivate->positionBeforePress : 0;
849 } else if (!swipePrivate->wasComplete) {
850 position = normalizedDistance;
851 } else {
852 position = distance > 0 ? normalizedDistance - 1.0 : normalizedDistance + 1.0;
853 }
854
855 if (swipePrivate->isTransitioning())
856 swipePrivate->transitionManager->cancel();
857 swipe.setPosition(position);
858 }
859 } else {
860 // The swipe wasn't initiated.
861 if (event->position().toPoint().y() < 0 || event->position().toPoint().y() > height) {
862 // The mouse went outside the vertical bounds of the control, so
863 // we should no longer consider it pressed.
864 q->setPressed(false);
865 }
866 }
867
868 event->accept();
869
870 return q->keepMouseGrab();
871}
872
873static const qreal exposeVelocityThreshold = 300.0;
874
875bool QQuickSwipeDelegatePrivate::handleMouseReleaseEvent(QQuickItem *item, QMouseEvent *event)
876{
877 Q_Q(QQuickSwipeDelegate);
878 QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&swipe);
879 swipePrivate->velocityCalculator.stopMeasuring(event->position().toPoint(), event->timestamp());
880
881 const bool hadGrabbedMouse = q->keepMouseGrab();
882 q->setKeepMouseGrab(false);
883
884 // QQuickSwipe::close() doesn't allow closing while pressed, but now we're releasing.
885 // So set the pressed state false if this release _could_ result in closing, so that check can be bypassed.
886 if (!qIsNull(swipePrivate->position))
887 q->setPressed(false);
888
889 // Animations for the background and contentItem delegates are typically
890 // only enabled when !control.down, so that the animations aren't running
891 // when the user is swiping. To ensure that the animations are enabled
892 // *before* the positions of these delegates change (via the swipe.setPosition() calls below),
893 // we must cancel the press. QQuickAbstractButton::mouseUngrabEvent() does this
894 // for us, but by then it's too late.
895 if (hadGrabbedMouse) {
896 // TODO: this is copied from QQuickAbstractButton::mouseUngrabEvent().
897 // Eventually it should be moved into a private helper so that we don't have to duplicate it.
898 q->setPressed(false);
899 stopPressRepeat();
900 stopPressAndHold();
901 emit q->canceled();
902 }
903
904 // Inform the given item that the mouse is released, in case it's an interactive child.
905 if (item != q && (swipePrivate->complete || swipePrivate->wasComplete))
906 forwardMouseEvent(event, item, item->mapFromScene(event->scenePosition()));
907
908 // The control can be exposed by either swiping past the halfway mark, or swiping fast enough.
909 const qreal swipeVelocity = swipePrivate->velocityCalculator.velocity().x();
910 if (swipePrivate->position > 0.5 ||
911 (swipePrivate->position > 0.0 && swipeVelocity > exposeVelocityThreshold)) {
912 swipePrivate->beginTransition(1.0);
913 swipePrivate->wasComplete = true;
914 } else if (swipePrivate->position < -0.5 ||
915 (swipePrivate->position < 0.0 && swipeVelocity < -exposeVelocityThreshold)) {
916 swipePrivate->beginTransition(-1.0);
917 swipePrivate->wasComplete = true;
918 } else if (!swipePrivate->isTransitioning()) {
919 // The position is either <= 0.5 or >= -0.5, so the position should go to 0.
920 // However, if the position was already 0 or close to it, we were just clicked,
921 // and we don't need to start a transition.
922 if (!qFuzzyIsNull(swipePrivate->position))
923 swipePrivate->beginTransition(0.0);
924 swipePrivate->wasComplete = false;
925 }
926
927 // Inform any QQuickSwipeDelegateAttached objects that the mouse is released.
928 attachedObjectsSetPressed(item, event->scenePosition(), false);
929
930 // Only consume child events if we had grabbed the mouse.
931 return hadGrabbedMouse;
932}
933
934/*! \internal
935 Send a localized copy of \a event with \a localPos to the \a destination item.
936*/
937void QQuickSwipeDelegatePrivate::forwardMouseEvent(QMouseEvent *event, QQuickItem *destination, QPointF localPos)
938{
939 Q_Q(QQuickSwipeDelegate);
940 QMutableSinglePointEvent localizedEvent(*event);
941 QMutableEventPoint::setPosition(localizedEvent.point(0), localPos);
942 QGuiApplication::sendEvent(destination, &localizedEvent);
943 q->setPressed(!localizedEvent.isAccepted());
944}
945
946/*! \internal
947 For each QQuickSwipeDelegateAttached object on children of \a item:
948 if \a scenePos is in the attachee (the item to which it's attached), then
949 set its \a pressed state. Unless \a cancel is \c true, when the state
950 transitions from pressed to released, also emit \l QQuickSwipeDelegateAttached::clicked().
951 Returns \c true if at least one relevant attached object was found.
952*/
953bool QQuickSwipeDelegatePrivate::attachedObjectsSetPressed(QQuickItem *item, QPointF scenePos, bool pressed, bool cancel)
954{
955 bool found = false;
956 QVarLengthArray<QQuickItem *, 16> itemAndChildren;
957 itemAndChildren.append(item);
958 for (int i = 0; i < itemAndChildren.size(); ++i) {
959 auto item = itemAndChildren.at(i);
960 auto posInItem = item->mapFromScene(scenePos);
961 if (item->contains(posInItem)) {
962 if (Attached *attached = attachedObject(item)) {
963 const bool wasPressed = attached->isPressed();
964 attached->setPressed(pressed);
965 if (wasPressed && !pressed && !cancel)
966 emit attached->clicked();
967 found = true;
968 }
969 }
970 for (auto child : item->childItems())
971 itemAndChildren.append(child);
972 }
973 return found;
974}
975
976static void warnIfHorizontallyAnchored(QQuickItem *item, const QString &itemName)
977{
978 if (!item)
979 return;
980
981 QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors;
982 if (anchors && (anchors->fill() || anchors->centerIn() || anchors->left().item || anchors->right().item)
983 && !item->property("_q_QQuickSwipeDelegate_warned").toBool()) {
984 qmlWarning(item) << QString::fromLatin1("SwipeDelegate: cannot use horizontal anchors with %1; unable to layout the item.").arg(itemName);
985 item->setProperty("_q_QQuickSwipeDelegate_warned", true);
986 }
987}
988
990{
991 warnIfHorizontallyAnchored(background, QStringLiteral("background"));
992 warnIfHorizontallyAnchored(contentItem, QStringLiteral("contentItem"));
993
994 // If the background and contentItem are repositioned due to a swipe,
995 // we don't want to call QQuickControlPrivate's implementation of this function,
996 // as it repositions the contentItem to be visible.
997 // However, we still want to position the contentItem vertically
998 // and resize it (in case the control was resized while open).
999 QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&swipe);
1000 if (!swipePrivate->complete) {
1001 QQuickItemDelegatePrivate::resizeContent();
1002 } else if (contentItem) {
1003 Q_Q(QQuickSwipeDelegate);
1004 contentItem->setY(q->topPadding());
1005 contentItem->setWidth(q->availableWidth());
1006 contentItem->setHeight(q->availableHeight());
1007 }
1008}
1009
1011{
1012 return QQuickTheme::palette(QQuickTheme::ListView);
1013}
1014
1015/*! \internal
1016 Recursively search right and/or left item tree of swipe delegate for any item that
1017 contains the \a event position.
1018
1019 Returns the first such item found, otherwise \c nullptr.
1020*/
1021QQuickItem *QQuickSwipeDelegatePrivate::getPressedItem(QQuickItem *childItem, QMouseEvent *event) const
1022{
1023 if (!childItem || !event)
1024 return nullptr;
1025
1026 QQuickItem *item = nullptr;
1027
1028 if (childItem->acceptedMouseButtons() &&
1029 childItem->contains(childItem->mapFromScene(event->scenePosition()))) {
1030 item = childItem;
1031 } else {
1032 const auto &childItems = childItem->childItems();
1033 for (const auto &child: childItems) {
1034 if ((item = getPressedItem(child, event)))
1035 break;
1036 }
1037 }
1038
1039 return item;
1040}
1041
1042QQuickSwipeDelegate::QQuickSwipeDelegate(QQuickItem *parent)
1043 : QQuickItemDelegate(*(new QQuickSwipeDelegatePrivate(this)), parent)
1044{
1045 // QQuickSwipeDelegate still depends on synthesized mouse events
1046 setAcceptTouchEvents(false);
1047}
1048
1049/*!
1050 \since QtQuick.Controls 2.2 (Qt 5.9)
1051 \qmlmethod void QtQuick.Controls::SwipeDelegate::swipe.open(enumeration side)
1052
1053 This method sets the \c position of the swipe so that it opens
1054 from the specified \a side.
1055
1056 Available values:
1057 \value SwipeDelegate.Left The \c position is set to \c 1, which makes the swipe open
1058 from the left. Either \c swipe.left or \c swipe.behind must
1059 have been specified; otherwise the call is ignored.
1060 \value SwipeDelegate.Right The \c position is set to \c -1, which makes the swipe open
1061 from the right. Either \c swipe.right or \c swipe.behind must
1062 have been specified; otherwise the call is ignored.
1063
1064 Any animations defined for the \l {Item::}{x} position of \l {Control::}{contentItem}
1065 and \l {Control::}{background} will be triggered.
1066
1067 \sa swipe, swipe.close()
1068*/
1069
1070/*!
1071 \since QtQuick.Controls 2.1 (Qt 5.8)
1072 \qmlmethod void QtQuick.Controls::SwipeDelegate::swipe.close()
1073
1074 This method sets the \c position of the swipe to \c 0. Any animations
1075 defined for the \l {Item::}{x} position of \l {Control::}{contentItem}
1076 and \l {Control::}{background} will be triggered.
1077
1078 \sa swipe, swipe.open()
1079*/
1080
1081/*!
1082 \since QtQuick.Controls 2.2 (Qt 5.9)
1083 \qmlsignal void QtQuick.Controls::SwipeDelegate::swipe.opened()
1084
1085 This signal is emitted when the delegate has been swiped open
1086 and the transition has finished.
1087
1088 It is useful for performing some action upon completion of a swipe.
1089 For example, it can be used to remove the delegate from the list
1090 that it is in.
1091
1092 \sa swipe, swipe.closed()
1093*/
1094
1095/*!
1096 \since QtQuick.Controls 2.2 (Qt 5.9)
1097 \qmlsignal void QtQuick.Controls::SwipeDelegate::swipe.closed()
1098
1099 This signal is emitted when the delegate has been swiped to closed
1100 and the transition has finished.
1101
1102 It is useful for performing some action upon cancellation of a swipe.
1103 For example, it can be used to cancel the removal of the delegate from
1104 the list that it is in.
1105
1106 \sa swipe, swipe.opened()
1107*/
1108
1109/*!
1110 \since QtQuick.Controls 2.1 (Qt 5.8)
1111 \qmlsignal void QtQuick.Controls::SwipeDelegate::swipe.completed()
1112
1113 This signal is emitted when \c swipe.complete becomes \c true.
1114
1115 It is useful for performing some action upon completion of a swipe.
1116 For example, it can be used to remove the delegate from the list
1117 that it is in.
1118
1119 \sa swipe
1120*/
1121
1122/*!
1123 \qmlproperty real QtQuick.Controls::SwipeDelegate::swipe.position
1124 \qmlproperty bool QtQuick.Controls::SwipeDelegate::swipe.complete
1125 \qmlproperty bool QtQuick.Controls::SwipeDelegate::swipe.enabled
1126 \qmlproperty Component QtQuick.Controls::SwipeDelegate::swipe.left
1127 \qmlproperty Component QtQuick.Controls::SwipeDelegate::swipe.behind
1128 \qmlproperty Component QtQuick.Controls::SwipeDelegate::swipe.right
1129 \qmlproperty Item QtQuick.Controls::SwipeDelegate::swipe.leftItem
1130 \qmlproperty Item QtQuick.Controls::SwipeDelegate::swipe.behindItem
1131 \qmlproperty Item QtQuick.Controls::SwipeDelegate::swipe.rightItem
1132 \qmlproperty Transition QtQuick.Controls::SwipeDelegate::swipe.transition
1133
1134 \table
1135 \header
1136 \li Name
1137 \li Description
1138 \row
1139 \li position
1140 \li This read-only property holds the position of the swipe relative to either
1141 side of the control. When this value reaches either
1142 \c -1.0 (left side) or \c 1.0 (right side) and the mouse button is
1143 released, \c complete will be \c true.
1144 \row
1145 \li complete
1146 \li This read-only property holds whether the control is fully exposed after
1147 having been swiped to the left or right.
1148
1149 When complete is \c true, any interactive items declared in \c left,
1150 \c right, or \c behind will receive mouse events.
1151 \row
1152 \li enabled
1153 \li This property determines whether or not the control can be swiped.
1154
1155 This property was added in QtQuick.Controls 2.2.
1156 \row
1157 \li left
1158 \li This property holds the left delegate.
1159
1160 The left delegate sits behind both \l {Control::}{contentItem} and
1161 \l {Control::}{background}. When the SwipeDelegate is swiped to the right,
1162 this item will be gradually revealed.
1163
1164 \include qquickswipedelegate-interaction.qdocinc
1165 \row
1166 \li behind
1167 \li This property holds the delegate that is shown when the
1168 SwipeDelegate is swiped to both the left and right.
1169
1170 As with the \c left and \c right delegates, it sits behind both
1171 \l {Control::}{contentItem} and \l {Control::}{background}. However, a
1172 SwipeDelegate whose \c behind has been set can be continuously swiped
1173 from either side, and will always show the same item.
1174
1175 \include qquickswipedelegate-interaction.qdocinc
1176 \row
1177 \li right
1178 \li This property holds the right delegate.
1179
1180 The right delegate sits behind both \l {Control::}{contentItem} and
1181 \l {Control::}{background}. When the SwipeDelegate is swiped to the left,
1182 this item will be gradually revealed.
1183
1184 \include qquickswipedelegate-interaction.qdocinc
1185 \row
1186 \li leftItem
1187 \li This read-only property holds the item instantiated from the \c left component.
1188
1189 If \c left has not been set, or the position hasn't changed since
1190 creation of the SwipeDelegate, this property will be \c null.
1191 \row
1192 \li behindItem
1193 \li This read-only property holds the item instantiated from the \c behind component.
1194
1195 If \c behind has not been set, or the position hasn't changed since
1196 creation of the SwipeDelegate, this property will be \c null.
1197 \row
1198 \li rightItem
1199 \li This read-only property holds the item instantiated from the \c right component.
1200
1201 If \c right has not been set, or the position hasn't changed since
1202 creation of the SwipeDelegate, this property will be \c null.
1203 \row
1204 \li transition
1205 \li This property holds the transition that is applied when a swipe is released,
1206 or \l swipe.open() or \l swipe.close() is called.
1207
1208 \snippet qtquickcontrols-swipedelegate-transition.qml 1
1209
1210 This property was added in Qt Quick Controls 2.2.
1211 \endtable
1212
1213 \sa {Control::}{contentItem}, {Control::}{background}, swipe.open(), swipe.close()
1214*/
1215QQuickSwipe *QQuickSwipeDelegate::swipe() const
1216{
1217 Q_D(const QQuickSwipeDelegate);
1218 return const_cast<QQuickSwipe*>(&d->swipe);
1219}
1220
1221QQuickSwipeDelegateAttached *QQuickSwipeDelegate::qmlAttachedProperties(QObject *object)
1222{
1223 return new QQuickSwipeDelegateAttached(object);
1224}
1225
1226static bool isChildOrGrandchildOf(QQuickItem *child, QQuickItem *item)
1227{
1228 return item && (child == item || item->isAncestorOf(child));
1229}
1230
1231bool QQuickSwipeDelegate::childMouseEventFilter(QQuickItem *child, QEvent *event)
1232{
1233 Q_D(QQuickSwipeDelegate);
1234 // The contentItem is, by default, usually a non-interactive item like Text, and
1235 // the same applies to the background. This means that simply stacking the left/right/behind
1236 // items before these items won't allow us to get mouse events when the control is not currently exposed
1237 // but has been previously. Therefore, we instead call setFiltersChildMouseEvents(true) in the constructor
1238 // and filter out child events only when the child is the left/right/behind item.
1239 const QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&d->swipe);
1240 if (!isChildOrGrandchildOf(child, swipePrivate->leftItem) && !isChildOrGrandchildOf(child, swipePrivate->behindItem)
1241 && !isChildOrGrandchildOf(child, swipePrivate->rightItem)) {
1242 return false;
1243 }
1244
1245 switch (event->type()) {
1246 case QEvent::MouseButtonPress: {
1247 return d->handleMousePressEvent(child, static_cast<QMouseEvent *>(event));
1248 } case QEvent::MouseMove: {
1249 return d->handleMouseMoveEvent(child, static_cast<QMouseEvent *>(event));
1250 } case QEvent::MouseButtonRelease: {
1251 // Make sure that the control gets release events if it has created child
1252 // items that are stealing events from it.
1253 QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
1254 QQuickItemDelegate::mouseReleaseEvent(mouseEvent);
1255 return d->handleMouseReleaseEvent(child, mouseEvent);
1256 } case QEvent::UngrabMouse: {
1257 // If the mouse was pressed over e.g. rightItem and then dragged down,
1258 // the ListView would eventually grab the mouse, at which point we must
1259 // clear the pressed flag so that it doesn't stay pressed after the release.
1260 Attached *attached = attachedObject(child);
1261 if (attached)
1262 attached->setPressed(false);
1263 return false;
1264 } default:
1265 return false;
1266 }
1267}
1268
1269// We only override this to set positionBeforePress;
1270// otherwise, it's the same as the base class implementation.
1271void QQuickSwipeDelegate::mousePressEvent(QMouseEvent *event)
1272{
1273 Q_D(QQuickSwipeDelegate);
1274 QQuickItemDelegate::mousePressEvent(event);
1275
1276 QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&d->swipe);
1277 if (!swipePrivate->enabled)
1278 return;
1279
1280 swipePrivate->positionBeforePress = swipePrivate->position;
1281 swipePrivate->velocityCalculator.startMeasuring(event->position().toPoint(), event->timestamp());
1282
1283 if (swipePrivate->complete) {
1284 d->pressedItem = d->getPressedItem(d->swipe.rightItem(), event);
1285 if (!d->pressedItem)
1286 d->pressedItem = d->getPressedItem(d->swipe.leftItem(), event);
1287 if (d->pressedItem)
1288 d->handleMousePressEvent(d->pressedItem, event);
1289 }
1290}
1291
1292void QQuickSwipeDelegate::mouseMoveEvent(QMouseEvent *event)
1293{
1294 Q_D(QQuickSwipeDelegate);
1295 if (filtersChildMouseEvents())
1296 d->handleMouseMoveEvent(this, event);
1297 else
1298 QQuickItemDelegate::mouseMoveEvent(event);
1299 if (d->pressedItem)
1300 d->handleMouseMoveEvent(d->pressedItem, event);
1301}
1302
1303void QQuickSwipeDelegate::mouseReleaseEvent(QMouseEvent *event)
1304{
1305 Q_D(QQuickSwipeDelegate);
1306 if (!filtersChildMouseEvents() || !d->handleMouseReleaseEvent(this, event))
1307 QQuickItemDelegate::mouseReleaseEvent(event);
1308
1309 if (d->pressedItem) {
1310 if (d->pressedItem->acceptedMouseButtons())
1311 d->handleMouseReleaseEvent(d->pressedItem, event);
1312 d->pressedItem = nullptr;
1313 }
1314}
1315
1316void QQuickSwipeDelegate::mouseUngrabEvent()
1317{
1318 Q_D(QQuickSwipeDelegate);
1319 setPressed(false);
1320
1321 auto item = d->swipe.rightItem();
1322 if (item) {
1323 if (auto control = qmlobject_cast<QQuickControl *>(item))
1324 QQuickControlPrivate::get(control)->handleUngrab();
1325 Attached *attached = attachedObject(item);
1326 if (attached)
1327 attached->setPressed(false);
1328 } else {
1329 item = d->swipe.leftItem();
1330 if (item) {
1331 if (auto control = qmlobject_cast<QQuickControl *>(item))
1332 QQuickControlPrivate::get(control)->handleUngrab();
1333 Attached *attached = attachedObject(item);
1334 if (attached)
1335 attached->setPressed(false);
1336 }
1337 }
1338
1339 d->pressedItem = nullptr;
1340}
1341
1342void QQuickSwipeDelegate::touchEvent(QTouchEvent *event)
1343{
1344 // Don't allow QQuickControl accept the touch event, because QQuickSwipeDelegate
1345 // is still based on synthesized mouse events
1346 event->ignore();
1347}
1348
1349void QQuickSwipeDelegate::componentComplete()
1350{
1351 Q_D(QQuickSwipeDelegate);
1352 QQuickItemDelegate::componentComplete();
1353 QQuickSwipePrivate::get(&d->swipe)->reposition(DontAnimatePosition);
1354}
1355
1356void QQuickSwipeDelegate::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
1357{
1358 Q_D(QQuickSwipeDelegate);
1359 QQuickControl::geometryChange(newGeometry, oldGeometry);
1360
1361 if (isComponentComplete() && !qFuzzyCompare(newGeometry.width(), oldGeometry.width())) {
1362 QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&d->swipe);
1363 swipePrivate->reposition(DontAnimatePosition);
1364 }
1365}
1366
1367QFont QQuickSwipeDelegate::defaultFont() const
1368{
1369 return QQuickTheme::font(QQuickTheme::ListView);
1370}
1371
1372#if QT_CONFIG(accessibility)
1373QAccessible::Role QQuickSwipeDelegate::accessibleRole() const
1374{
1375 return QAccessible::ListItem;
1376}
1377#endif
1378
1380{
1381 Q_DECLARE_PUBLIC(QQuickSwipeDelegateAttached)
1382
1383public:
1384 // True when left/right/behind is non-interactive and is pressed.
1385 bool pressed = false;
1386};
1387
1388/*!
1389 \since QtQuick.Controls 2.1 (Qt 5.8)
1390 \qmlattachedsignal QtQuick.Controls::SwipeDelegate::clicked()
1391
1392 This signal can be attached to a non-interactive item declared in
1393 \c swipe.left, \c swipe.right, or \c swipe.behind, in order to react to
1394 clicks. Items can only be clicked when \c swipe.complete is \c true.
1395
1396 For interactive controls (such as \l Button) declared in these
1397 items, use their respective \c clicked() signal instead.
1398
1399 To respond to clicks on the SwipeDelegate itself, use its
1400 \l {AbstractButton::}{clicked()} signal.
1401
1402 \note See the documentation for \l pressed for information on
1403 how to use the event-related properties correctly.
1404
1405 \sa pressed
1406*/
1407
1408QQuickSwipeDelegateAttached::QQuickSwipeDelegateAttached(QObject *object)
1409 : QObject(*(new QQuickSwipeDelegateAttachedPrivate), object)
1410{
1411 QQuickItem *item = qobject_cast<QQuickItem *>(object);
1412 if (item) {
1413 // This allows us to be notified when an otherwise non-interactive item
1414 // is pressed and clicked. The alternative is much more more complex:
1415 // iterating through children that contain the event pos and finding
1416 // the first one with an attached object.
1417 item->setAcceptedMouseButtons(Qt::AllButtons);
1418 } else {
1419 qWarning() << "SwipeDelegate attached property must be attached to an object deriving from Item";
1420 }
1421}
1422
1423/*!
1424 \since QtQuick.Controls 2.1 (Qt 5.8)
1425 \qmlattachedproperty bool QtQuick.Controls::SwipeDelegate::pressed
1426 \readonly
1427
1428 This property can be attached to a non-interactive item declared in
1429 \c swipe.left, \c swipe.right, or \c swipe.behind, in order to detect if it
1430 is pressed. Items can only be pressed when \c swipe.complete is \c true.
1431
1432 For example:
1433
1434 \code
1435 swipe.right: Label {
1436 anchors.right: parent.right
1437 height: parent.height
1438 text: "Action"
1439 color: "white"
1440 padding: 12
1441 background: Rectangle {
1442 color: SwipeDelegate.pressed ? Qt.darker("tomato", 1.1) : "tomato"
1443 }
1444 }
1445 \endcode
1446
1447 It is possible to have multiple items which individually receive mouse and
1448 touch events. For example, to have two actions in the \c swipe.right item,
1449 use the following code:
1450
1451 \code
1452 swipe.right: Row {
1453 anchors.right: parent.right
1454 height: parent.height
1455
1456 Label {
1457 id: moveLabel
1458 text: qsTr("Move")
1459 color: "white"
1460 verticalAlignment: Label.AlignVCenter
1461 padding: 12
1462 height: parent.height
1463
1464 SwipeDelegate.onClicked: console.log("Moving...")
1465
1466 background: Rectangle {
1467 color: moveLabel.SwipeDelegate.pressed ? Qt.darker("#ffbf47", 1.1) : "#ffbf47"
1468 }
1469 }
1470 Label {
1471 id: deleteLabel
1472 text: qsTr("Delete")
1473 color: "white"
1474 verticalAlignment: Label.AlignVCenter
1475 padding: 12
1476 height: parent.height
1477
1478 SwipeDelegate.onClicked: console.log("Deleting...")
1479
1480 background: Rectangle {
1481 color: deleteLabel.SwipeDelegate.pressed ? Qt.darker("tomato", 1.1) : "tomato"
1482 }
1483 }
1484 }
1485 \endcode
1486
1487 Note how the \c color assignment in each \l {Control::}{background} item
1488 qualifies the attached property with the \c id of the label. This
1489 is important; using the attached properties on an item causes that item
1490 to accept events. Suppose we had left out the \c id in the previous example:
1491
1492 \code
1493 color: SwipeDelegate.pressed ? Qt.darker("tomato", 1.1) : "tomato"
1494 \endcode
1495
1496 The \l Rectangle background item is a child of the label, so it naturally
1497 receives events before it. In practice, this means that the background
1498 color will change, but the \c onClicked handler in the label will never
1499 get called.
1500
1501 For interactive controls (such as \l Button) declared in these
1502 items, use their respective \c pressed property instead.
1503
1504 For presses on the SwipeDelegate itself, use its
1505 \l {AbstractButton::}{pressed} property.
1506
1507 \sa clicked()
1508*/
1509bool QQuickSwipeDelegateAttached::isPressed() const
1510{
1511 Q_D(const QQuickSwipeDelegateAttached);
1512 return d->pressed;
1513}
1514
1515void QQuickSwipeDelegateAttached::setPressed(bool pressed)
1516{
1517 Q_D(QQuickSwipeDelegateAttached);
1518 if (pressed == d->pressed)
1519 return;
1520
1521 d->pressed = pressed;
1522 emit pressedChanged();
1523}
1524
1525QT_END_NAMESPACE
1526
1527#include "moc_qquickswipe_p.cpp"
1528#include "moc_qquickswipedelegate_p.cpp"
bool handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event)
void forwardMouseEvent(QMouseEvent *event, QQuickItem *destination, QPointF localPos)
QPalette defaultPalette() const override
bool attachedObjectsSetPressed(QQuickItem *item, QPointF scenePos, bool pressed, bool cancel=false)
QQuickItem * getPressedItem(QQuickItem *childItem, QMouseEvent *event) const
bool handleMouseReleaseEvent(QQuickItem *item, QMouseEvent *event)
bool handleMousePressEvent(QQuickItem *item, QMouseEvent *event)
QQuickVelocityCalculator velocityCalculator
QQuickItem * showRelevantItemForPosition(qreal position)
QScopedPointer< QQuickSwipeTransitionManager > transitionManager
void reposition(PositionAnimation animationPolicy)
QQuickTransition * transition
QQuickItem * createDelegateItem(QQmlComponent *component)
QQuickItem * createRelevantItemForDistance(qreal distance)
void beginTransition(qreal position)
QQuickSwipeDelegate * control
QQuickSwipeTransitionManager(QQuickSwipe *swipe)
void transition(QQuickTransition *transition, qreal position)
QQuickSwipeDelegateAttached Attached
Attached * attachedObject(QQuickItem *item)
static bool isChildOrGrandchildOf(QQuickItem *child, QQuickItem *item)
static const qreal exposeVelocityThreshold
static void warnIfHorizontallyAnchored(QQuickItem *item, const QString &itemName)