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
51 QT_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
106 namespace {
107 typedef QQuickSwipeDelegateAttached Attached;
108
attachedObject(QQuickItem * item)109 Attached *attachedObject(QQuickItem *item) {
110 return qobject_cast<Attached*>(qmlAttachedPropertiesObject<QQuickSwipeDelegate>(item, false));
111 }
112
113 enum PositionAnimation {
114 DontAnimatePosition,
115 AnimatePosition
116 };
117 }
118
119 class QQuickSwipeTransitionManager : public QQuickTransitionManager
120 {
121 public:
122 QQuickSwipeTransitionManager(QQuickSwipe *swipe);
123
124 void transition(QQuickTransition *transition, qreal position);
125
126 protected:
127 void finished() override;
128
129 private:
130 QQuickSwipe *m_swipe = nullptr;
131 };
132
133 class QQuickSwipePrivate : public QObjectPrivate
134 {
135 Q_DECLARE_PUBLIC(QQuickSwipe)
136
137 public:
QQuickSwipePrivate(QQuickSwipeDelegate * control)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
QQuickSwipeTransitionManager(QQuickSwipe * swipe)183 QQuickSwipeTransitionManager::QQuickSwipeTransitionManager(QQuickSwipe *swipe)
184 : m_swipe(swipe)
185 {
186 }
187
transition(QQuickTransition * transition,qreal position)188 void 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, m_swipe);
203 }
204
finished()205 void QQuickSwipeTransitionManager::finished()
206 {
207 QQuickSwipePrivate::get(m_swipe)->finishTransition();
208 }
209
get(QQuickSwipe * swipe)210 QQuickSwipePrivate *QQuickSwipePrivate::get(QQuickSwipe *swipe)
211 {
212 return swipe->d_func();
213 }
214
createDelegateItem(QQmlComponent * component)215 QQuickItem *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*>(component->beginCreate(context));
227 if (item) {
228 item->setParentItem(control);
229 component->completeCreate();
230 }
231 return item;
232 }
233
showRelevantItemForPosition(qreal position)234 QQuickItem *QQuickSwipePrivate::showRelevantItemForPosition(qreal position)
235 {
236 if (qFuzzyIsNull(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
createRelevantItemForDistance(qreal distance)257 QQuickItem *QQuickSwipePrivate::createRelevantItemForDistance(qreal distance)
258 {
259 if (qFuzzyIsNull(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(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(distance) < leftItem->width()) /* b) */
290 || (rightItem && positionBeforePress == -1.0 && qAbs(distance) > rightItem->width())) /* c) */ {
291 createLeftItem();
292 return leftItem;
293 }
294 }
295
296 return nullptr;
297 }
298
reposition(PositionAnimation animationPolicy)299 void 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("x", contentItemX);
310 if (QQuickItem *background = control->background())
311 background->setProperty("x", 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
createLeftItem()320 void QQuickSwipePrivate::createLeftItem()
321 {
322 if (!leftItem) {
323 Q_Q(QQuickSwipe);
324 q->setLeftItem(createDelegateItem(left));
325 if (!leftItem)
326 qmlWarning(control) << "Failed to create left item:" << left->errors();
327 }
328 }
329
createBehindItem()330 void QQuickSwipePrivate::createBehindItem()
331 {
332 if (!behindItem) {
333 Q_Q(QQuickSwipe);
334 q->setBehindItem(createDelegateItem(behind));
335 if (!behindItem)
336 qmlWarning(control) << "Failed to create behind item:" << behind->errors();
337 }
338 }
339
createRightItem()340 void QQuickSwipePrivate::createRightItem()
341 {
342 if (!rightItem) {
343 Q_Q(QQuickSwipe);
344 q->setRightItem(createDelegateItem(right));
345 if (!rightItem)
346 qmlWarning(control) << "Failed to create right item:" << right->errors();
347 }
348 }
349
createAndShowLeftItem()350 void QQuickSwipePrivate::createAndShowLeftItem()
351 {
352 createLeftItem();
353
354 if (leftItem)
355 leftItem->setVisible(true);
356
357 if (rightItem)
358 rightItem->setVisible(false);
359 }
360
createAndShowBehindItem()361 void QQuickSwipePrivate::createAndShowBehindItem()
362 {
363 createBehindItem();
364
365 if (behindItem)
366 behindItem->setVisible(true);
367 }
368
createAndShowRightItem()369 void 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
warnAboutMixingDelegates()382 void QQuickSwipePrivate::warnAboutMixingDelegates()
383 {
384 qmlWarning(control) << "cannot set both behind and left/right properties";
385 }
386
warnAboutSettingDelegatesWhileVisible()387 void QQuickSwipePrivate::warnAboutSettingDelegatesWhileVisible()
388 {
389 qmlWarning(control) << "left/right/behind properties may only be set when swipe.position is 0";
390 }
391
hasDelegates() const392 bool QQuickSwipePrivate::hasDelegates() const
393 {
394 return left || right || behind;
395 }
396
isTransitioning() const397 bool QQuickSwipePrivate::isTransitioning() const
398 {
399 return transitionManager && transitionManager->isRunning();
400 }
401
beginTransition(qreal newPosition)402 void 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(new QQuickSwipeTransitionManager(q));
413
414 transitionManager->transition(transition, newPosition);
415 }
416
finishTransition()417 void QQuickSwipePrivate::finishTransition()
418 {
419 Q_Q(QQuickSwipe);
420 q->setComplete(qFuzzyCompare(qAbs(position), qreal(1.0)));
421 if (complete)
422 emit q->opened();
423 else
424 emit q->closed();
425 }
426
QQuickSwipe(QQuickSwipeDelegate * control)427 QQuickSwipe::QQuickSwipe(QQuickSwipeDelegate *control)
428 : QObject(*(new QQuickSwipePrivate(control)))
429 {
430 }
431
left() const432 QQmlComponent *QQuickSwipe::left() const
433 {
434 Q_D(const QQuickSwipe);
435 return d->left;
436 }
437
setLeft(QQmlComponent * left)438 void 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->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
behind() const466 QQmlComponent *QQuickSwipe::behind() const
467 {
468 Q_D(const QQuickSwipe);
469 return d->behind;
470 }
471
setBehind(QQmlComponent * behind)472 void 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->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
right() const500 QQmlComponent *QQuickSwipe::right() const
501 {
502 Q_D(const QQuickSwipe);
503 return d->right;
504 }
505
setRight(QQmlComponent * right)506 void 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->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
leftItem() const534 QQuickItem *QQuickSwipe::leftItem() const
535 {
536 Q_D(const QQuickSwipe);
537 return d->leftItem;
538 }
539
setLeftItem(QQuickItem * item)540 void 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->leftItem->z()))
553 d->leftItem->setZ(-2);
554 }
555
556 emit leftItemChanged();
557 }
558
behindItem() const559 QQuickItem *QQuickSwipe::behindItem() const
560 {
561 Q_D(const QQuickSwipe);
562 return d->behindItem;
563 }
564
setBehindItem(QQuickItem * item)565 void 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->behindItem->z()))
578 d->behindItem->setZ(-2);
579 }
580
581 emit behindItemChanged();
582 }
583
rightItem() const584 QQuickItem *QQuickSwipe::rightItem() const
585 {
586 Q_D(const QQuickSwipe);
587 return d->rightItem;
588 }
589
setRightItem(QQuickItem * item)590 void 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->rightItem->z()))
603 d->rightItem->setZ(-2);
604 }
605
606 emit rightItemChanged();
607 }
608
position() const609 qreal QQuickSwipe::position() const
610 {
611 Q_D(const QQuickSwipe);
612 return d->position;
613 }
614
setPosition(qreal position)615 void QQuickSwipe::setPosition(qreal position)
616 {
617 Q_D(QQuickSwipe);
618 const qreal adjustedPosition = qBound<qreal>(-1.0, position, 1.0);
619 if (adjustedPosition == d->position)
620 return;
621
622 d->position = adjustedPosition;
623 d->reposition(AnimatePosition);
624 emit positionChanged();
625 }
626
isComplete() const627 bool QQuickSwipe::isComplete() const
628 {
629 Q_D(const QQuickSwipe);
630 return d->complete;
631 }
632
setComplete(bool complete)633 void 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
isEnabled() const645 bool QQuickSwipe::isEnabled() const
646 {
647 Q_D(const QQuickSwipe);
648 return d->enabled;
649 }
650
setEnabled(bool enabled)651 void 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
transition() const661 QQuickTransition *QQuickSwipe::transition() const
662 {
663 Q_D(const QQuickSwipe);
664 return d->transition;
665 }
666
setTransition(QQuickTransition * transition)667 void 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
open(QQuickSwipeDelegate::Side side)677 void QQuickSwipe::open(QQuickSwipeDelegate::Side side)
678 {
679 Q_D(QQuickSwipe);
680 if (qFuzzyCompare(qAbs(d->position), 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(side);
689 d->wasComplete = true;
690 d->velocityCalculator.reset();
691 d->positionBeforePress = d->position;
692 }
693
close()694 void QQuickSwipe::close()
695 {
696 Q_D(QQuickSwipe);
697 if (qFuzzyIsNull(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(0.0);
706 d->wasComplete = false;
707 d->positionBeforePress = 0.0;
708 d->velocityCalculator.reset();
709 }
710
QQuickSwipeDelegatePrivate(QQuickSwipeDelegate * control)711 QQuickSwipeDelegatePrivate::QQuickSwipeDelegatePrivate(QQuickSwipeDelegate *control)
712 : swipe(control)
713 {
714 }
715
handleMousePressEvent(QQuickItem * item,QMouseEvent * event)716 bool QQuickSwipeDelegatePrivate::handleMousePressEvent(QQuickItem *item, QMouseEvent *event)
717 {
718 Q_Q(QQuickSwipeDelegate);
719 QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&swipe);
720 // If the position is 0, we want to handle events ourselves - we don't want child items to steal them.
721 // This code will only get called when a child item has been created;
722 // events will go through the regular channels (mousePressEvent()) until then.
723 if (qFuzzyIsNull(swipePrivate->position)) {
724 q->mousePressEvent(event);
725 // The press point could be incorrect if the press happened over a child item,
726 // so we correct it after calling the base class' mousePressEvent(), rather
727 // than having to duplicate its code just so we can set the pressPoint.
728 setPressPoint(item->mapToItem(q, event->pos()));
729 return true;
730 }
731
732 // The position is non-zero, this press could be either for a delegate or the control itself
733 // (the control can be clicked to e.g. close the swipe). Either way, we must begin measuring
734 // mouse movement in case it turns into a swipe, in which case we grab the mouse.
735 swipePrivate->positionBeforePress = swipePrivate->position;
736 swipePrivate->velocityCalculator.startMeasuring(event->pos(), event->timestamp());
737 setPressPoint(item->mapToItem(q, event->pos()));
738
739 // When a delegate uses the attached properties and signals, it declares that it wants mouse events.
740 Attached *attached = attachedObject(item);
741 if (attached) {
742 attached->setPressed(true);
743 // Stop the event from propagating, as QQuickItem explicitly ignores events.
744 event->accept();
745 return true;
746 }
747
748 return false;
749 }
750
handleMouseMoveEvent(QQuickItem * item,QMouseEvent * event)751 bool QQuickSwipeDelegatePrivate::handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event)
752 {
753 Q_Q(QQuickSwipeDelegate);
754
755 if (holdTimer > 0) {
756 if (QLineF(pressPoint, event->localPos()).length() > QGuiApplication::styleHints()->startDragDistance())
757 stopPressAndHold();
758 }
759
760 // The delegate can still be pressed when swipe.enabled is false,
761 // but the mouse moving shouldn't have any effect on swipe.position.
762 QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&swipe);
763 if (!swipePrivate->enabled)
764 return false;
765
766 // Protect against division by zero.
767 if (width == 0)
768 return false;
769
770 // Don't bother reacting to events if we don't have any delegates.
771 if (!swipePrivate->left && !swipePrivate->right && !swipePrivate->behind)
772 return false;
773
774 // Don't handle move events for the control if it wasn't pressed.
775 if (item == q && !pressed)
776 return false;
777
778 const QPointF mappedEventPos = item->mapToItem(q, event->pos());
779 const qreal distance = (mappedEventPos - pressPoint).x();
780 if (!q->keepMouseGrab()) {
781 // Taken from QQuickDrawerPrivate::grabMouse; see comments there.
782 int threshold = qMax(20, QGuiApplication::styleHints()->startDragDistance() + 5);
783 const bool overThreshold = QQuickWindowPrivate::dragOverThreshold(distance, Qt::XAxis, event, threshold);
784 if (window && overThreshold) {
785 QQuickItem *grabber = q->window()->mouseGrabberItem();
786 if (!grabber || !grabber->keepMouseGrab()) {
787 q->grabMouse();
788 q->setKeepMouseGrab(true);
789 q->setPressed(true);
790 swipe.setComplete(false);
791
792 if (Attached *attached = attachedObject(item))
793 attached->setPressed(false);
794 }
795 }
796 }
797
798 if (q->keepMouseGrab()) {
799 // Ensure we don't try to calculate a position when the user tried to drag
800 // to the left when the left item is already exposed, and vice versa.
801 // The code below assumes that the drag is valid, so if we don't have this check,
802 // the wrong items are visible and the swiping wraps.
803 if (swipePrivate->behind
804 || ((swipePrivate->left || swipePrivate->right)
805 && (qFuzzyIsNull(swipePrivate->positionBeforePress)
806 || (swipePrivate->positionBeforePress == -1.0 && distance >= 0.0)
807 || (swipePrivate->positionBeforePress == 1.0 && distance <= 0.0)))) {
808
809 // We must instantiate the items here so that we can calculate the
810 // position against the width of the relevant item.
811 QQuickItem *relevantItem = swipePrivate->createRelevantItemForDistance(distance);
812 // If there isn't any relevant item, the user may have swiped back to the 0 position,
813 // or they swiped back to a position that is equal to positionBeforePress.
814 const qreal normalizedDistance = relevantItem ? distance / relevantItem->width() : 0.0;
815 qreal position = 0;
816
817 // If the control was exposed before the drag begun, the distance should be inverted.
818 // For example, if the control had been swiped to the right, the position would be 1.0.
819 // If the control was then swiped to the left by a distance of -20 pixels, the normalized
820 // distance might be -0.2, for example, which cannot be used as the position; the swipe
821 // started from the right, so we account for that by adding the position.
822 if (qFuzzyIsNull(normalizedDistance)) {
823 // There are two cases when the normalizedDistance can be 0,
824 // and we must distinguish between them:
825 //
826 // a) The swipe returns to the position that it was at before the press event.
827 // In this case, the distance will be 0.
828 // There would have been many position changes in the meantime, so we can't just
829 // ignore the move event; we have to set position to what it was before the press.
830 //
831 // b) If the position was at, 1.0, for example, and the control was then swiped
832 // to the left by the exact width of the left item, there won't be any relevant item
833 // (because the swipe's position would be at 0.0). In turn, the normalizedDistance
834 // would be 0 (because of the lack of a relevant item), but the distance will be non-zero.
835 position = qFuzzyIsNull(distance) ? swipePrivate->positionBeforePress : 0;
836 } else if (!swipePrivate->wasComplete) {
837 position = normalizedDistance;
838 } else {
839 position = distance > 0 ? normalizedDistance - 1.0 : normalizedDistance + 1.0;
840 }
841
842 if (swipePrivate->isTransitioning())
843 swipePrivate->transitionManager->cancel();
844 swipe.setPosition(position);
845 }
846 } else {
847 // The swipe wasn't initiated.
848 if (event->pos().y() < 0 || event->pos().y() > height) {
849 // The mouse went outside the vertical bounds of the control, so
850 // we should no longer consider it pressed.
851 q->setPressed(false);
852 }
853 }
854
855 event->accept();
856
857 return q->keepMouseGrab();
858 }
859
860 static const qreal exposeVelocityThreshold = 300.0;
861
handleMouseReleaseEvent(QQuickItem * item,QMouseEvent * event)862 bool QQuickSwipeDelegatePrivate::handleMouseReleaseEvent(QQuickItem *item, QMouseEvent *event)
863 {
864 Q_Q(QQuickSwipeDelegate);
865 QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&swipe);
866 swipePrivate->velocityCalculator.stopMeasuring(event->pos(), event->timestamp());
867
868 const bool hadGrabbedMouse = q->keepMouseGrab();
869 q->setKeepMouseGrab(false);
870
871 // Animations for the background and contentItem delegates are typically
872 // only enabled when !control.down, so that the animations aren't running
873 // when the user is swiping. To ensure that the animations are enabled
874 // *before* the positions of these delegates change (via the swipe.setPosition() calls below),
875 // we must cancel the press. QQuickAbstractButton::mouseUngrabEvent() does this
876 // for us, but by then it's too late.
877 if (hadGrabbedMouse) {
878 // TODO: this is copied from QQuickAbstractButton::mouseUngrabEvent().
879 // Eventually it should be moved into a private helper so that we don't have to duplicate it.
880 q->setPressed(false);
881 stopPressRepeat();
882 stopPressAndHold();
883 emit q->canceled();
884 }
885
886 // The control can be exposed by either swiping past the halfway mark, or swiping fast enough.
887 const qreal swipeVelocity = swipePrivate->velocityCalculator.velocity().x();
888 if (swipePrivate->position > 0.5 ||
889 (swipePrivate->position > 0.0 && swipeVelocity > exposeVelocityThreshold)) {
890 swipePrivate->beginTransition(1.0);
891 swipePrivate->wasComplete = true;
892 } else if (swipePrivate->position < -0.5 ||
893 (swipePrivate->position < 0.0 && swipeVelocity < -exposeVelocityThreshold)) {
894 swipePrivate->beginTransition(-1.0);
895 swipePrivate->wasComplete = true;
896 } else if (!swipePrivate->isTransitioning()) {
897 // The position is either <= 0.5 or >= -0.5, so the position should go to 0.
898 // However, if the position was already 0 or close to it, we were just clicked,
899 // and we don't need to start a transition.
900 if (!qFuzzyIsNull(swipePrivate->position))
901 swipePrivate->beginTransition(0.0);
902 swipePrivate->wasComplete = false;
903 }
904
905 if (Attached *attached = attachedObject(item)) {
906 const bool wasPressed = attached->isPressed();
907 if (wasPressed) {
908 attached->setPressed(false);
909 emit attached->clicked();
910 }
911 }
912
913 // Only consume child events if we had grabbed the mouse.
914 return hadGrabbedMouse;
915 }
916
warnIfHorizontallyAnchored(QQuickItem * item,const QString & itemName)917 static void warnIfHorizontallyAnchored(QQuickItem *item, const QString &itemName)
918 {
919 if (!item)
920 return;
921
922 QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors;
923 if (anchors && (anchors->fill() || anchors->centerIn() || anchors->left().item || anchors->right().item)
924 && !item->property("_q_QQuickSwipeDelegate_warned").toBool()) {
925 qmlWarning(item) << QString::fromLatin1("SwipeDelegate: cannot use horizontal anchors with %1; unable to layout the item.").arg(itemName);
926 item->setProperty("_q_QQuickSwipeDelegate_warned", true);
927 }
928 }
929
resizeContent()930 void QQuickSwipeDelegatePrivate::resizeContent()
931 {
932 warnIfHorizontallyAnchored(background, QStringLiteral("background"));
933 warnIfHorizontallyAnchored(contentItem, QStringLiteral("contentItem"));
934
935 // If the background and contentItem are repositioned due to a swipe,
936 // we don't want to call QQuickControlPrivate's implementation of this function,
937 // as it repositions the contentItem to be visible.
938 // However, we still want to resize the control vertically.
939 QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&swipe);
940 if (!swipePrivate->complete) {
941 QQuickItemDelegatePrivate::resizeContent();
942 } else if (contentItem) {
943 Q_Q(QQuickSwipeDelegate);
944 contentItem->setY(q->topPadding());
945 contentItem->setHeight(q->availableHeight());
946 }
947 }
948
QQuickSwipeDelegate(QQuickItem * parent)949 QQuickSwipeDelegate::QQuickSwipeDelegate(QQuickItem *parent)
950 : QQuickItemDelegate(*(new QQuickSwipeDelegatePrivate(this)), parent)
951 {
952 }
953
954 /*!
955 \since QtQuick.Controls 2.2 (Qt 5.9)
956 \qmlmethod void QtQuick.Controls::SwipeDelegate::swipe.open(enumeration side)
957
958 This method sets the \c position of the swipe so that it opens
959 from the specified \a side.
960
961 Available values:
962 \value SwipeDelegate.Left The \c position is set to \c 1, which makes the swipe open
963 from the left. Either \c swipe.left or \c swipe.behind must
964 have been specified; otherwise the call is ignored.
965 \value SwipeDelegate.Right The \c position is set to \c -1, which makes the swipe open
966 from the right. Either \c swipe.right or \c swipe.behind must
967 have been specified; otherwise the call is ignored.
968
969 Any animations defined for the \l {Item::}{x} position of \l {Control::}{contentItem}
970 and \l {Control::}{background} will be triggered.
971
972 \sa swipe, swipe.close()
973 */
974
975 /*!
976 \since QtQuick.Controls 2.1 (Qt 5.8)
977 \qmlmethod void QtQuick.Controls::SwipeDelegate::swipe.close()
978
979 This method sets the \c position of the swipe to \c 0. Any animations
980 defined for the \l {Item::}{x} position of \l {Control::}{contentItem}
981 and \l {Control::}{background} will be triggered.
982
983 \sa swipe, swipe.open()
984 */
985
986 /*!
987 \since QtQuick.Controls 2.2 (Qt 5.9)
988 \qmlsignal void QtQuick.Controls::SwipeDelegate::swipe.opened()
989
990 This signal is emitted when the delegate has been swiped open
991 and the transition has finished.
992
993 It is useful for performing some action upon completion of a swipe.
994 For example, it can be used to remove the delegate from the list
995 that it is in.
996
997 \sa swipe, swipe.closed()
998 */
999
1000 /*!
1001 \since QtQuick.Controls 2.2 (Qt 5.9)
1002 \qmlsignal void QtQuick.Controls::SwipeDelegate::swipe.closed()
1003
1004 This signal is emitted when the delegate has been swiped to closed
1005 and the transition has finished.
1006
1007 It is useful for performing some action upon cancellation of a swipe.
1008 For example, it can be used to cancel the removal of the delegate from
1009 the list that it is in.
1010
1011 \sa swipe, swipe.opened()
1012 */
1013
1014 /*!
1015 \since QtQuick.Controls 2.1 (Qt 5.8)
1016 \qmlsignal void QtQuick.Controls::SwipeDelegate::swipe.completed()
1017
1018 This signal is emitted when \c swipe.complete becomes \c true.
1019
1020 It is useful for performing some action upon completion of a swipe.
1021 For example, it can be used to remove the delegate from the list
1022 that it is in.
1023
1024 \sa swipe
1025 */
1026
1027 /*!
1028 \qmlproperty real QtQuick.Controls::SwipeDelegate::swipe.position
1029 \qmlproperty bool QtQuick.Controls::SwipeDelegate::swipe.complete
1030 \qmlproperty bool QtQuick.Controls::SwipeDelegate::swipe.enabled
1031 \qmlproperty Component QtQuick.Controls::SwipeDelegate::swipe.left
1032 \qmlproperty Component QtQuick.Controls::SwipeDelegate::swipe.behind
1033 \qmlproperty Component QtQuick.Controls::SwipeDelegate::swipe.right
1034 \qmlproperty Item QtQuick.Controls::SwipeDelegate::swipe.leftItem
1035 \qmlproperty Item QtQuick.Controls::SwipeDelegate::swipe.behindItem
1036 \qmlproperty Item QtQuick.Controls::SwipeDelegate::swipe.rightItem
1037 \qmlproperty Transition QtQuick.Controls::SwipeDelegate::swipe.transition
1038
1039 \table
1040 \header
1041 \li Name
1042 \li Description
1043 \row
1044 \li position
1045 \li This read-only property holds the position of the swipe relative to either
1046 side of the control. When this value reaches either
1047 \c -1.0 (left side) or \c 1.0 (right side) and the mouse button is
1048 released, \c complete will be \c true.
1049 \row
1050 \li complete
1051 \li This read-only property holds whether the control is fully exposed after
1052 having been swiped to the left or right.
1053
1054 When complete is \c true, any interactive items declared in \c left,
1055 \c right, or \c behind will receive mouse events.
1056 \row
1057 \li enabled
1058 \li This property determines whether or not the control can be swiped.
1059
1060 This property was added in QtQuick.Controls 2.2.
1061 \row
1062 \li left
1063 \li This property holds the left delegate.
1064
1065 The left delegate sits behind both \l {Control::}{contentItem} and
1066 \l {Control::}{background}. When the SwipeDelegate is swiped to the right,
1067 this item will be gradually revealed.
1068
1069 \include qquickswipedelegate-interaction.qdocinc
1070 \row
1071 \li behind
1072 \li This property holds the delegate that is shown when the
1073 SwipeDelegate is swiped to both the left and right.
1074
1075 As with the \c left and \c right delegates, it sits behind both
1076 \l {Control::}{contentItem} and \l {Control::}{background}. However, a
1077 SwipeDelegate whose \c behind has been set can be continuously swiped
1078 from either side, and will always show the same item.
1079
1080 \include qquickswipedelegate-interaction.qdocinc
1081 \row
1082 \li right
1083 \li This property holds the right delegate.
1084
1085 The right delegate sits behind both \l {Control::}{contentItem} and
1086 \l {Control::}{background}. When the SwipeDelegate is swiped to the left,
1087 this item will be gradually revealed.
1088
1089 \include qquickswipedelegate-interaction.qdocinc
1090 \row
1091 \li leftItem
1092 \li This read-only property holds the item instantiated from the \c left component.
1093
1094 If \c left has not been set, or the position hasn't changed since
1095 creation of the SwipeDelegate, this property will be \c null.
1096 \row
1097 \li behindItem
1098 \li This read-only property holds the item instantiated from the \c behind component.
1099
1100 If \c behind has not been set, or the position hasn't changed since
1101 creation of the SwipeDelegate, this property will be \c null.
1102 \row
1103 \li rightItem
1104 \li This read-only property holds the item instantiated from the \c right component.
1105
1106 If \c right has not been set, or the position hasn't changed since
1107 creation of the SwipeDelegate, this property will be \c null.
1108 \row
1109 \li transition
1110 \li This property holds the transition that is applied when a swipe is released,
1111 or \l swipe.open() or \l swipe.close() is called.
1112
1113 \snippet qtquickcontrols2-swipedelegate-transition.qml 1
1114
1115 This property was added in Qt Quick Controls 2.2.
1116 \endtable
1117
1118 \sa {Control::}{contentItem}, {Control::}{background}, swipe.open(), swipe.close()
1119 */
swipe() const1120 QQuickSwipe *QQuickSwipeDelegate::swipe() const
1121 {
1122 Q_D(const QQuickSwipeDelegate);
1123 return const_cast<QQuickSwipe*>(&d->swipe);
1124 }
1125
qmlAttachedProperties(QObject * object)1126 QQuickSwipeDelegateAttached *QQuickSwipeDelegate::qmlAttachedProperties(QObject *object)
1127 {
1128 return new QQuickSwipeDelegateAttached(object);
1129 }
1130
isChildOrGrandchildOf(QQuickItem * child,QQuickItem * item)1131 static bool isChildOrGrandchildOf(QQuickItem *child, QQuickItem *item)
1132 {
1133 return item && (child == item || item->isAncestorOf(child));
1134 }
1135
childMouseEventFilter(QQuickItem * child,QEvent * event)1136 bool QQuickSwipeDelegate::childMouseEventFilter(QQuickItem *child, QEvent *event)
1137 {
1138 Q_D(QQuickSwipeDelegate);
1139 // The contentItem is, by default, usually a non-interactive item like Text, and
1140 // the same applies to the background. This means that simply stacking the left/right/behind
1141 // items before these items won't allow us to get mouse events when the control is not currently exposed
1142 // but has been previously. Therefore, we instead call setFiltersChildMouseEvents(true) in the constructor
1143 // and filter out child events only when the child is the left/right/behind item.
1144 const QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&d->swipe);
1145 if (!isChildOrGrandchildOf(child, swipePrivate->leftItem) && !isChildOrGrandchildOf(child, swipePrivate->behindItem)
1146 && !isChildOrGrandchildOf(child, swipePrivate->rightItem)) {
1147 return false;
1148 }
1149
1150 switch (event->type()) {
1151 case QEvent::MouseButtonPress: {
1152 return d->handleMousePressEvent(child, static_cast<QMouseEvent *>(event));
1153 } case QEvent::MouseMove: {
1154 return d->handleMouseMoveEvent(child, static_cast<QMouseEvent *>(event));
1155 } case QEvent::MouseButtonRelease: {
1156 // Make sure that the control gets release events if it has created child
1157 // items that are stealing events from it.
1158 QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
1159 QQuickItemDelegate::mouseReleaseEvent(mouseEvent);
1160 return d->handleMouseReleaseEvent(child, mouseEvent);
1161 } case QEvent::UngrabMouse: {
1162 // If the mouse was pressed over e.g. rightItem and then dragged down,
1163 // the ListView would eventually grab the mouse, at which point we must
1164 // clear the pressed flag so that it doesn't stay pressed after the release.
1165 Attached *attached = attachedObject(child);
1166 if (attached)
1167 attached->setPressed(false);
1168 return false;
1169 } default:
1170 return false;
1171 }
1172 }
1173
1174 // We only override this to set positionBeforePress;
1175 // otherwise, it's the same as the base class implementation.
mousePressEvent(QMouseEvent * event)1176 void QQuickSwipeDelegate::mousePressEvent(QMouseEvent *event)
1177 {
1178 Q_D(QQuickSwipeDelegate);
1179 QQuickItemDelegate::mousePressEvent(event);
1180
1181 QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&d->swipe);
1182 if (!swipePrivate->enabled)
1183 return;
1184
1185 swipePrivate->positionBeforePress = swipePrivate->position;
1186 swipePrivate->velocityCalculator.startMeasuring(event->pos(), event->timestamp());
1187 }
1188
mouseMoveEvent(QMouseEvent * event)1189 void QQuickSwipeDelegate::mouseMoveEvent(QMouseEvent *event)
1190 {
1191 Q_D(QQuickSwipeDelegate);
1192 if (filtersChildMouseEvents())
1193 d->handleMouseMoveEvent(this, event);
1194 else
1195 QQuickItemDelegate::mouseMoveEvent(event);
1196 }
1197
mouseReleaseEvent(QMouseEvent * event)1198 void QQuickSwipeDelegate::mouseReleaseEvent(QMouseEvent *event)
1199 {
1200 Q_D(QQuickSwipeDelegate);
1201 if (!filtersChildMouseEvents() || !d->handleMouseReleaseEvent(this, event))
1202 QQuickItemDelegate::mouseReleaseEvent(event);
1203 }
1204
touchEvent(QTouchEvent * event)1205 void QQuickSwipeDelegate::touchEvent(QTouchEvent *event)
1206 {
1207 // Don't allow QQuickControl accept the touch event, because QQuickSwipeDelegate
1208 // is still based on synthesized mouse events
1209 event->ignore();
1210 }
1211
componentComplete()1212 void QQuickSwipeDelegate::componentComplete()
1213 {
1214 Q_D(QQuickSwipeDelegate);
1215 QQuickItemDelegate::componentComplete();
1216 QQuickSwipePrivate::get(&d->swipe)->reposition(DontAnimatePosition);
1217 }
1218
geometryChanged(const QRectF & newGeometry,const QRectF & oldGeometry)1219 void QQuickSwipeDelegate::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1220 {
1221 Q_D(QQuickSwipeDelegate);
1222 QQuickControl::geometryChanged(newGeometry, oldGeometry);
1223
1224 if (isComponentComplete() && !qFuzzyCompare(newGeometry.width(), oldGeometry.width())) {
1225 QQuickSwipePrivate *swipePrivate = QQuickSwipePrivate::get(&d->swipe);
1226 swipePrivate->reposition(DontAnimatePosition);
1227 }
1228 }
1229
defaultFont() const1230 QFont QQuickSwipeDelegate::defaultFont() const
1231 {
1232 return QQuickTheme::font(QQuickTheme::ListView);
1233 }
1234
defaultPalette() const1235 QPalette QQuickSwipeDelegate::defaultPalette() const
1236 {
1237 return QQuickTheme::palette(QQuickTheme::ListView);
1238 }
1239
1240 #if QT_CONFIG(accessibility)
accessibleRole() const1241 QAccessible::Role QQuickSwipeDelegate::accessibleRole() const
1242 {
1243 return QAccessible::ListItem;
1244 }
1245 #endif
1246
1247 class QQuickSwipeDelegateAttachedPrivate : public QObjectPrivate
1248 {
1249 Q_DECLARE_PUBLIC(QQuickSwipeDelegateAttached)
1250
1251 public:
1252 // True when left/right/behind is non-interactive and is pressed.
1253 bool pressed = false;
1254 };
1255
1256 /*!
1257 \since QtQuick.Controls 2.1 (Qt 5.8)
1258 \qmlattachedsignal QtQuick.Controls::SwipeDelegate::clicked()
1259
1260 This signal can be attached to a non-interactive item declared in
1261 \c swipe.left, \c swipe.right, or \c swipe.behind, in order to react to
1262 clicks. Items can only be clicked when \c swipe.complete is \c true.
1263
1264 For interactive controls (such as \l Button) declared in these
1265 items, use their respective \c clicked() signal instead.
1266
1267 To respond to clicks on the SwipeDelegate itself, use its
1268 \l {AbstractButton::}{clicked()} signal.
1269
1270 \note See the documentation for \l pressed for information on
1271 how to use the event-related properties correctly.
1272
1273 \sa pressed
1274 */
1275
QQuickSwipeDelegateAttached(QObject * object)1276 QQuickSwipeDelegateAttached::QQuickSwipeDelegateAttached(QObject *object)
1277 : QObject(*(new QQuickSwipeDelegateAttachedPrivate), object)
1278 {
1279 QQuickItem *item = qobject_cast<QQuickItem *>(object);
1280 if (item) {
1281 // This allows us to be notified when an otherwise non-interactive item
1282 // is pressed and clicked. The alternative is much more more complex:
1283 // iterating through children that contain the event pos and finding
1284 // the first one with an attached object.
1285 item->setAcceptedMouseButtons(Qt::AllButtons);
1286 } else {
1287 qWarning() << "Attached properties of SwipeDelegate must be accessed through an Item";
1288 }
1289 }
1290
1291 /*!
1292 \since QtQuick.Controls 2.1 (Qt 5.8)
1293 \qmlattachedproperty bool QtQuick.Controls::SwipeDelegate::pressed
1294 \readonly
1295
1296 This property can be attached to a non-interactive item declared in
1297 \c swipe.left, \c swipe.right, or \c swipe.behind, in order to detect if it
1298 is pressed. Items can only be pressed when \c swipe.complete is \c true.
1299
1300 For example:
1301
1302 \code
1303 swipe.right: Label {
1304 anchors.right: parent.right
1305 height: parent.height
1306 text: "Action"
1307 color: "white"
1308 padding: 12
1309 background: Rectangle {
1310 color: SwipeDelegate.pressed ? Qt.darker("tomato", 1.1) : "tomato"
1311 }
1312 }
1313 \endcode
1314
1315 It is possible to have multiple items which individually receive mouse and
1316 touch events. For example, to have two actions in the \c swipe.right item,
1317 use the following code:
1318
1319 \code
1320 swipe.right: Row {
1321 anchors.right: parent.right
1322 height: parent.height
1323
1324 Label {
1325 id: moveLabel
1326 text: qsTr("Move")
1327 color: "white"
1328 verticalAlignment: Label.AlignVCenter
1329 padding: 12
1330 height: parent.height
1331
1332 SwipeDelegate.onClicked: console.log("Moving...")
1333
1334 background: Rectangle {
1335 color: moveLabel.SwipeDelegate.pressed ? Qt.darker("#ffbf47", 1.1) : "#ffbf47"
1336 }
1337 }
1338 Label {
1339 id: deleteLabel
1340 text: qsTr("Delete")
1341 color: "white"
1342 verticalAlignment: Label.AlignVCenter
1343 padding: 12
1344 height: parent.height
1345
1346 SwipeDelegate.onClicked: console.log("Deleting...")
1347
1348 background: Rectangle {
1349 color: deleteLabel.SwipeDelegate.pressed ? Qt.darker("tomato", 1.1) : "tomato"
1350 }
1351 }
1352 }
1353 \endcode
1354
1355 Note how the \c color assignment in each \l {Control::}{background} item
1356 qualifies the attached property with the \c id of the label. This
1357 is important; using the attached properties on an item causes that item
1358 to accept events. Suppose we had left out the \c id in the previous example:
1359
1360 \code
1361 color: SwipeDelegate.pressed ? Qt.darker("tomato", 1.1) : "tomato"
1362 \endcode
1363
1364 The \l Rectangle background item is a child of the label, so it naturally
1365 receives events before it. In practice, this means that the background
1366 color will change, but the \c onClicked handler in the label will never
1367 get called.
1368
1369 For interactive controls (such as \l Button) declared in these
1370 items, use their respective \c pressed property instead.
1371
1372 For presses on the SwipeDelegate itself, use its
1373 \l {AbstractButton::}{pressed} property.
1374
1375 \sa clicked()
1376 */
isPressed() const1377 bool QQuickSwipeDelegateAttached::isPressed() const
1378 {
1379 Q_D(const QQuickSwipeDelegateAttached);
1380 return d->pressed;
1381 }
1382
setPressed(bool pressed)1383 void QQuickSwipeDelegateAttached::setPressed(bool pressed)
1384 {
1385 Q_D(QQuickSwipeDelegateAttached);
1386 if (pressed == d->pressed)
1387 return;
1388
1389 d->pressed = pressed;
1390 emit pressedChanged();
1391 }
1392
1393 QT_END_NAMESPACE
1394