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
4#include "qquickswitchdelegate_p.h"
5
6#include "qquickitemdelegate_p_p.h"
7
8QT_BEGIN_NAMESPACE
9
10/*!
11 \qmltype SwitchDelegate
12 \inherits ItemDelegate
13//! \instantiates QQuickSwitchDelegate
14 \inqmlmodule QtQuick.Controls
15 \since 5.7
16 \ingroup qtquickcontrols-delegates
17 \brief Item delegate with a switch indicator that can be toggled on or off.
18
19 \image qtquickcontrols-switchdelegate.gif
20
21 SwitchDelegate presents an item delegate that can be toggled on (checked) or
22 off (unchecked). Switch delegates are typically used to select one or more
23 options from a set of options. For smaller sets of options, or for options
24 that need to be uniquely identifiable, consider using \l Switch instead.
25
26 SwitchDelegate inherits its API from \l ItemDelegate, which is inherited
27 from \l AbstractButton. For instance, you can set \l {AbstractButton::text}{text},
28 and react to \l {AbstractButton::clicked}{clicks} using the \l AbstractButton
29 API. The state of the switch delegate can be set with the
30 \l {AbstractButton::}{checked} property.
31
32 \code
33 ListView {
34 model: ["Option 1", "Option 2", "Option 3"]
35 delegate: SwitchDelegate {
36 text: modelData
37 }
38 }
39 \endcode
40
41 \sa {Customizing SwitchDelegate}, {Delegate Controls}
42*/
43
44class QQuickSwitchDelegatePrivate : public QQuickItemDelegatePrivate
45{
46 Q_DECLARE_PUBLIC(QQuickSwitchDelegate)
47
48public:
49 qreal positionAt(const QPointF &point) const;
50
51 bool canDrag(const QPointF &movePoint) const;
52 bool handleMove(const QPointF &point, ulong timestamp) override;
53 bool handleRelease(const QPointF &point, ulong timestamp) override;
54
55 QPalette defaultPalette() const override { return QQuickTheme::palette(scope: QQuickTheme::ListView); }
56
57 qreal position = 0;
58};
59
60qreal QQuickSwitchDelegatePrivate::positionAt(const QPointF &point) const
61{
62 Q_Q(const QQuickSwitchDelegate);
63 qreal pos = 0.0;
64 if (indicator)
65 pos = indicator->mapFromItem(item: q, point).x() / indicator->width();
66 if (q->isMirrored())
67 return 1.0 - pos;
68 return pos;
69}
70
71bool QQuickSwitchDelegatePrivate::canDrag(const QPointF &movePoint) const
72{
73 // don't start dragging the handle unless the initial press was at the indicator,
74 // or the drag has reached the indicator area. this prevents unnatural jumps when
75 // dragging far outside the indicator.
76 const qreal pressPos = positionAt(point: pressPoint);
77 const qreal movePos = positionAt(point: movePoint);
78 return (pressPos >= 0.0 && pressPos <= 1.0) || (movePos >= 0.0 && movePos <= 1.0);
79}
80
81bool QQuickSwitchDelegatePrivate::handleMove(const QPointF &point, ulong timestamp)
82{
83 Q_Q(QQuickSwitchDelegate);
84 QQuickItemDelegatePrivate::handleMove(point, timestamp);
85 if (q->keepMouseGrab() || q->keepTouchGrab())
86 q->setPosition(positionAt(point));
87 return true;
88}
89
90bool QQuickSwitchDelegatePrivate::handleRelease(const QPointF &point, ulong timestamp)
91{
92 Q_Q(QQuickSwitchDelegate);
93 QQuickItemDelegatePrivate::handleRelease(point, timestamp);
94 q->setKeepMouseGrab(false);
95 q->setKeepTouchGrab(false);
96 return true;
97}
98
99QQuickSwitchDelegate::QQuickSwitchDelegate(QQuickItem *parent)
100 : QQuickItemDelegate(*(new QQuickSwitchDelegatePrivate), parent)
101{
102 Q_D(QQuickSwitchDelegate);
103 d->keepPressed = true;
104 setCheckable(true);
105}
106
107/*!
108 \qmlproperty real QtQuick.Controls::SwitchDelegate::position
109 \readonly
110
111 \input includes/qquickswitch.qdocinc position
112*/
113qreal QQuickSwitchDelegate::position() const
114{
115 Q_D(const QQuickSwitchDelegate);
116 return d->position;
117}
118
119void QQuickSwitchDelegate::setPosition(qreal position)
120{
121 Q_D(QQuickSwitchDelegate);
122 position = qBound<qreal>(min: 0.0, val: position, max: 1.0);
123 if (qFuzzyCompare(p1: d->position, p2: position))
124 return;
125
126 d->position = position;
127 emit positionChanged();
128 emit visualPositionChanged();
129}
130
131/*!
132 \qmlproperty real QtQuick.Controls::SwitchDelegate::visualPosition
133 \readonly
134
135 \input includes/qquickswitch.qdocinc visualPosition
136*/
137qreal QQuickSwitchDelegate::visualPosition() const
138{
139 Q_D(const QQuickSwitchDelegate);
140 if (isMirrored())
141 return 1.0 - d->position;
142 return d->position;
143}
144
145void QQuickSwitchDelegate::mouseMoveEvent(QMouseEvent *event)
146{
147 Q_D(QQuickSwitchDelegate);
148 if (!keepMouseGrab()) {
149 const QPointF movePoint = event->position();
150 if (d->canDrag(movePoint))
151 setKeepMouseGrab(QQuickWindowPrivate::dragOverThreshold(d: movePoint.x() - d->pressPoint.x(), axis: Qt::XAxis, event));
152 }
153 QQuickItemDelegate::mouseMoveEvent(event);
154}
155
156#if QT_CONFIG(quicktemplates2_multitouch)
157void QQuickSwitchDelegate::touchEvent(QTouchEvent *event)
158{
159 Q_D(QQuickSwitchDelegate);
160 if (!keepTouchGrab() && event->type() == QEvent::TouchUpdate) {
161 for (const QTouchEvent::TouchPoint &point : event->points()) {
162 if (point.id() != d->touchId || point.state() != QEventPoint::Updated)
163 continue;
164 if (d->canDrag(movePoint: point.position()))
165 setKeepTouchGrab(QQuickWindowPrivate::dragOverThreshold(d: point.position().x() - d->pressPoint.x(), axis: Qt::XAxis, tp: &point));
166 }
167 }
168 QQuickItemDelegate::touchEvent(event);
169}
170#endif
171
172QFont QQuickSwitchDelegate::defaultFont() const
173{
174 return QQuickTheme::font(scope: QQuickTheme::ListView);
175}
176
177void QQuickSwitchDelegate::mirrorChange()
178{
179 QQuickItemDelegate::mirrorChange();
180 emit visualPositionChanged();
181}
182
183void QQuickSwitchDelegate::nextCheckState()
184{
185 Q_D(QQuickSwitchDelegate);
186 if (keepMouseGrab() || keepTouchGrab()) {
187 d->toggle(value: d->position > 0.5);
188 // the checked state might not change => force a position update to
189 // avoid that the handle is left somewhere in the middle (QTBUG-57944)
190 setPosition(d->checked ? 1.0 : 0.0);
191 } else {
192 QQuickItemDelegate::nextCheckState();
193 }
194}
195
196void QQuickSwitchDelegate::buttonChange(ButtonChange change)
197{
198 Q_D(QQuickSwitchDelegate);
199 if (change == ButtonCheckedChange)
200 setPosition(d->checked ? 1.0 : 0.0);
201 else
202 QQuickAbstractButton::buttonChange(change);
203}
204
205QT_END_NAMESPACE
206
207#include "moc_qquickswitchdelegate_p.cpp"
208

source code of qtdeclarative/src/quicktemplates/qquickswitchdelegate.cpp