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

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