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

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