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

source code of qtquickcontrols2/src/quicktemplates2/qquickswipedelegate.cpp