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

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