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