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 | |
45 | QT_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 | |
80 | class QQuickSwitchPrivate : public QQuickAbstractButtonPrivate |
81 | { |
82 | Q_DECLARE_PUBLIC(QQuickSwitch) |
83 | |
84 | public: |
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 | |
94 | qreal 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 | |
105 | bool 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 | |
115 | void 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 | |
123 | void QQuickSwitchPrivate::handleRelease(const QPointF &point) |
124 | { |
125 | Q_Q(QQuickSwitch); |
126 | QQuickAbstractButtonPrivate::handleRelease(point); |
127 | q->setKeepMouseGrab(false); |
128 | q->setKeepTouchGrab(false); |
129 | } |
130 | |
131 | QQuickSwitch::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 | */ |
145 | qreal QQuickSwitch::position() const |
146 | { |
147 | Q_D(const QQuickSwitch); |
148 | return d->position; |
149 | } |
150 | |
151 | void 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 | */ |
169 | qreal QQuickSwitch::visualPosition() const |
170 | { |
171 | Q_D(const QQuickSwitch); |
172 | if (isMirrored()) |
173 | return 1.0 - d->position; |
174 | return d->position; |
175 | } |
176 | |
177 | void 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) |
189 | void 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 | |
204 | void QQuickSwitch::mirrorChange() |
205 | { |
206 | QQuickAbstractButton::mirrorChange(); |
207 | emit visualPositionChanged(); |
208 | } |
209 | |
210 | void 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 | |
223 | void 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 | |
232 | QFont QQuickSwitch::defaultFont() const |
233 | { |
234 | return QQuickTheme::font(scope: QQuickTheme::Switch); |
235 | } |
236 | |
237 | QPalette QQuickSwitch::defaultPalette() const |
238 | { |
239 | return QQuickTheme::palette(scope: QQuickTheme::Switch); |
240 | } |
241 | |
242 | QT_END_NAMESPACE |
243 | |