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//! \instantiates 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
33 \code
34 ColumnLayout {
35 Switch {
36 text: qsTr("Wi-Fi")
37 }
38 Switch {
39 text: qsTr("Bluetooth")
40 }
41 }
42 \endcode
43
44 \sa {Customizing Switch}, {Button Controls}
45*/
46
47class QQuickSwitchPrivate : public QQuickAbstractButtonPrivate
48{
49 Q_DECLARE_PUBLIC(QQuickSwitch)
50
51public:
52 qreal positionAt(const QPointF &point) const;
53
54 bool canDrag(const QPointF &movePoint) const;
55 bool handleMove(const QPointF &point, ulong timestamp) override;
56 bool handleRelease(const QPointF &point, ulong timestamp) override;
57
58 QPalette defaultPalette() const override { return QQuickTheme::palette(scope: QQuickTheme::Switch); }
59
60 qreal position = 0;
61};
62
63qreal QQuickSwitchPrivate::positionAt(const QPointF &point) const
64{
65 Q_Q(const QQuickSwitch);
66 qreal pos = 0.0;
67 if (indicator)
68 pos = indicator->mapFromItem(item: q, point).x() / indicator->width();
69 if (q->isMirrored())
70 return 1.0 - pos;
71 return pos;
72}
73
74bool QQuickSwitchPrivate::canDrag(const QPointF &movePoint) const
75{
76 // don't start dragging the handle unless the initial press was at the indicator,
77 // or the drag has reached the indicator area. this prevents unnatural jumps when
78 // dragging far outside the indicator.
79 const qreal pressPos = positionAt(point: pressPoint);
80 const qreal movePos = positionAt(point: movePoint);
81 return (pressPos >= 0.0 && pressPos <= 1.0) || (movePos >= 0.0 && movePos <= 1.0);
82}
83
84bool QQuickSwitchPrivate::handleMove(const QPointF &point, ulong timestamp)
85{
86 Q_Q(QQuickSwitch);
87 QQuickAbstractButtonPrivate::handleMove(point, timestamp);
88 if (q->keepMouseGrab() || q->keepTouchGrab())
89 q->setPosition(positionAt(point));
90 return true;
91}
92
93bool QQuickSwitchPrivate::handleRelease(const QPointF &point, ulong timestamp)
94{
95 Q_Q(QQuickSwitch);
96 QQuickAbstractButtonPrivate::handleRelease(point, timestamp);
97 q->setKeepMouseGrab(false);
98 q->setKeepTouchGrab(false);
99 return true;
100}
101
102QQuickSwitch::QQuickSwitch(QQuickItem *parent)
103 : QQuickAbstractButton(*(new QQuickSwitchPrivate), parent)
104{
105 Q_D(QQuickSwitch);
106 d->keepPressed = true;
107 setCheckable(true);
108}
109
110/*!
111 \qmlproperty real QtQuick.Controls::Switch::position
112 \readonly
113
114 \input includes/qquickswitch.qdocinc position
115*/
116qreal QQuickSwitch::position() const
117{
118 Q_D(const QQuickSwitch);
119 return d->position;
120}
121
122void QQuickSwitch::setPosition(qreal position)
123{
124 Q_D(QQuickSwitch);
125 position = qBound<qreal>(min: 0.0, val: position, max: 1.0);
126 if (qFuzzyCompare(p1: d->position, p2: position))
127 return;
128
129 d->position = position;
130 emit positionChanged();
131 emit visualPositionChanged();
132}
133
134/*!
135 \qmlproperty real QtQuick.Controls::Switch::visualPosition
136 \readonly
137
138 \input includes/qquickswitch.qdocinc visualPosition
139*/
140qreal QQuickSwitch::visualPosition() const
141{
142 Q_D(const QQuickSwitch);
143 if (isMirrored())
144 return 1.0 - d->position;
145 return d->position;
146}
147
148void QQuickSwitch::mouseMoveEvent(QMouseEvent *event)
149{
150 Q_D(QQuickSwitch);
151 if (!keepMouseGrab()) {
152 const QPointF movePoint = event->position();
153 if (d->canDrag(movePoint))
154 setKeepMouseGrab(QQuickWindowPrivate::dragOverThreshold(d: movePoint.x() - d->pressPoint.x(), axis: Qt::XAxis, event));
155 }
156 QQuickAbstractButton::mouseMoveEvent(event);
157}
158
159#if QT_CONFIG(quicktemplates2_multitouch)
160void QQuickSwitch::touchEvent(QTouchEvent *event)
161{
162 Q_D(QQuickSwitch);
163 if (!keepTouchGrab() && event->type() == QEvent::TouchUpdate) {
164 for (const QTouchEvent::TouchPoint &point : event->points()) {
165 if (point.id() != d->touchId || point.state() != QEventPoint::Updated)
166 continue;
167 if (d->canDrag(movePoint: point.position()))
168 setKeepTouchGrab(QQuickWindowPrivate::dragOverThreshold(d: point.position().x() - d->pressPoint.x(), axis: Qt::XAxis, tp: &point));
169 }
170 }
171 QQuickAbstractButton::touchEvent(event);
172}
173#endif
174
175void QQuickSwitch::mirrorChange()
176{
177 QQuickAbstractButton::mirrorChange();
178 emit visualPositionChanged();
179}
180
181void QQuickSwitch::nextCheckState()
182{
183 Q_D(QQuickSwitch);
184 if (keepMouseGrab() || keepTouchGrab()) {
185 d->toggle(value: d->position > 0.5);
186 // the checked state might not change => force a position update to
187 // avoid that the handle is left somewhere in the middle (QTBUG-57944)
188 setPosition(d->checked ? 1.0 : 0.0);
189 } else {
190 QQuickAbstractButton::nextCheckState();
191 }
192}
193
194void QQuickSwitch::buttonChange(ButtonChange change)
195{
196 Q_D(QQuickSwitch);
197 if (change == ButtonCheckedChange)
198 setPosition(d->checked ? 1.0 : 0.0);
199 else
200 QQuickAbstractButton::buttonChange(change);
201}
202
203QFont QQuickSwitch::defaultFont() const
204{
205 return QQuickTheme::font(scope: QQuickTheme::Switch);
206}
207
208QT_END_NAMESPACE
209
210#include "moc_qquickswitch_p.cpp"
211

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