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
41 QT_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
77 class QQuickSwitchDelegatePrivate : public QQuickItemDelegatePrivate
78 {
79 Q_DECLARE_PUBLIC(QQuickSwitchDelegate)
80
81 public:
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
positionAt(const QPointF & point) const91 qreal QQuickSwitchDelegatePrivate::positionAt(const QPointF &point) const
92 {
93 Q_Q(const QQuickSwitchDelegate);
94 qreal pos = 0.0;
95 if (indicator)
96 pos = indicator->mapFromItem(q, point).x() / indicator->width();
97 if (q->isMirrored())
98 return 1.0 - pos;
99 return pos;
100 }
101
canDrag(const QPointF & movePoint) const102 bool 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(pressPoint);
108 const qreal movePos = positionAt(movePoint);
109 return (pressPos >= 0.0 && pressPos <= 1.0) || (movePos >= 0.0 && movePos <= 1.0);
110 }
111
handleMove(const QPointF & point)112 void 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
handleRelease(const QPointF & point)120 void QQuickSwitchDelegatePrivate::handleRelease(const QPointF &point)
121 {
122 Q_Q(QQuickSwitchDelegate);
123 QQuickItemDelegatePrivate::handleRelease(point);
124 q->setKeepMouseGrab(false);
125 q->setKeepTouchGrab(false);
126 }
127
QQuickSwitchDelegate(QQuickItem * parent)128 QQuickSwitchDelegate::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 */
position() const142 qreal QQuickSwitchDelegate::position() const
143 {
144 Q_D(const QQuickSwitchDelegate);
145 return d->position;
146 }
147
setPosition(qreal position)148 void QQuickSwitchDelegate::setPosition(qreal position)
149 {
150 Q_D(QQuickSwitchDelegate);
151 position = qBound<qreal>(0.0, position, 1.0);
152 if (qFuzzyCompare(d->position, 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 */
visualPosition() const166 qreal QQuickSwitchDelegate::visualPosition() const
167 {
168 Q_D(const QQuickSwitchDelegate);
169 if (isMirrored())
170 return 1.0 - d->position;
171 return d->position;
172 }
173
mouseMoveEvent(QMouseEvent * event)174 void 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(movePoint.x() - d->pressPoint.x(), Qt::XAxis, event));
181 }
182 QQuickItemDelegate::mouseMoveEvent(event);
183 }
184
185 #if QT_CONFIG(quicktemplates2_multitouch)
touchEvent(QTouchEvent * event)186 void 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(point.pos()))
194 setKeepTouchGrab(QQuickWindowPrivate::dragOverThreshold(point.pos().x() - d->pressPoint.x(), Qt::XAxis, &point));
195 }
196 }
197 QQuickItemDelegate::touchEvent(event);
198 }
199 #endif
200
defaultFont() const201 QFont QQuickSwitchDelegate::defaultFont() const
202 {
203 return QQuickTheme::font(QQuickTheme::ListView);
204 }
205
defaultPalette() const206 QPalette QQuickSwitchDelegate::defaultPalette() const
207 {
208 return QQuickTheme::palette(QQuickTheme::ListView);
209 }
210
mirrorChange()211 void QQuickSwitchDelegate::mirrorChange()
212 {
213 QQuickItemDelegate::mirrorChange();
214 emit visualPositionChanged();
215 }
216
nextCheckState()217 void QQuickSwitchDelegate::nextCheckState()
218 {
219 Q_D(QQuickSwitchDelegate);
220 if (keepMouseGrab() || keepTouchGrab()) {
221 d->toggle(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
buttonChange(ButtonChange change)230 void 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
239 QT_END_NAMESPACE
240