Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickflipable.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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// Qt-Security score:significant reason:default
4
6#include "qquickitem_p.h"
8
9#include <QtQml/qqmlinfo.h>
10
11#include <QtCore/qpointer.h>
12
14
15// XXX todo - i think this needs work and a bit of a re-think
16
18{
20public:
22
23 void setTransform(const QTransform &t) {
24 transform = t;
25 update();
26 }
28 *matrix *= transform;
29 }
30private:
32};
33
53
54/*!
55 \qmltype Flipable
56 \nativetype QQuickFlipable
57 \inqmlmodule QtQuick
58 \inherits Item
59 \ingroup qtquick-input
60 \ingroup qtquick-containers
61 \brief Provides a surface that can be flipped.
62
63 Flipable is an item that can be visibly "flipped" between its front and
64 back sides, like a card. It may used together with \l Rotation, \l State
65 and \l Transition types to produce a flipping effect.
66
67 The \l front and \l back properties are used to hold the items that are
68 shown respectively on the front and back sides of the flipable item.
69
70 \section1 Example Usage
71
72 The following example shows a Flipable item that flips whenever it is
73 clicked, rotating about the y-axis.
74
75 This flipable item has a \c flipped boolean property that is toggled
76 whenever the MouseArea within the flipable is clicked. When
77 \c flipped is true, the item changes to the "back" state; in this
78 state, the \c angle of the \l Rotation item is changed to 180
79 degrees to produce the flipping effect. When \c flipped is false, the
80 item reverts to the default state, in which the \c angle value is 0.
81
82 \snippet qml/flipable/flipable.qml 0
83
84 \image flipable.gif
85
86 The \l Transition creates the animation that changes the angle over
87 four seconds. When the item changes between its "back" and
88 default states, the NumberAnimation animates the angle between
89 its old and new values.
90
91 See \l {Qt Quick States} for details on state changes and the default
92 state, and \l {Animation and Transitions in Qt Quick} for more information on how
93 animations work within transitions.
94
95 \sa {customitems/flipable}{UI Components: Flipable Example}
96*/
97QQuickFlipable::QQuickFlipable(QQuickItem *parent)
98: QQuickItem(*(new QQuickFlipablePrivate), parent)
99{
100}
101
102QQuickFlipable::~QQuickFlipable()
103{
104}
105
106/*!
107 \qmlproperty Item QtQuick::Flipable::front
108 \qmlproperty Item QtQuick::Flipable::back
109
110 The front and back sides of the flipable.
111*/
112
113QQuickItem *QQuickFlipable::front() const
114{
115 Q_D(const QQuickFlipable);
116 return d->front;
117}
118
119void QQuickFlipable::setFront(QQuickItem *front)
120{
121 Q_D(QQuickFlipable);
122 if (d->front) {
123 qmlWarning(this) << tr("front is a write-once property");
124 return;
125 }
126 d->front = front;
127 d->front->setParentItem(this);
128 if (Back == d->current) {
129 d->front->setOpacity(0.);
130 d->front->setEnabled(false);
131 }
132 emit frontChanged();
133}
134
135QQuickItem *QQuickFlipable::back()
136{
137 Q_D(const QQuickFlipable);
138 return d->back;
139}
140
141void QQuickFlipable::setBack(QQuickItem *back)
142{
143 Q_D(QQuickFlipable);
144 if (d->back) {
145 qmlWarning(this) << tr("back is a write-once property");
146 return;
147 }
148 if (back == nullptr)
149 return;
150 d->back = back;
151 d->back->setParentItem(this);
152
153 d->backTransform = new QQuickLocalTransform(d->back);
154 d->backTransform->prependToItem(d->back);
155
156 if (Front == d->current) {
157 d->back->setOpacity(0.);
158 d->back->setEnabled(false);
159 }
160
161 connect(back, SIGNAL(widthChanged()),
162 this, SLOT(retransformBack()));
163 connect(back, SIGNAL(heightChanged()),
164 this, SLOT(retransformBack()));
165 emit backChanged();
166}
167
168void QQuickFlipable::retransformBack()
169{
170 Q_D(QQuickFlipable);
171 if (d->current == QQuickFlipable::Back && d->back)
172 d->setBackTransform();
173}
174
175/*!
176 \qmlproperty enumeration QtQuick::Flipable::side
177
178 The side of the Flipable currently visible. Possible values are \c
179 Flipable.Front and \c Flipable.Back.
180*/
181QQuickFlipable::Side QQuickFlipable::side() const
182{
183 Q_D(const QQuickFlipable);
184
185 const_cast<QQuickFlipablePrivate *>(d)->updateSide();
186 return d->current;
187}
188
189bool QQuickFlipablePrivate::transformChanged(QQuickItem *transformedItem)
190{
191 Q_Q(QQuickFlipable);
192
193 if (!sideDirty) {
194 sideDirty = true;
195 q->polish();
196 }
197
198 return QQuickItemPrivate::transformChanged(transformedItem);
199}
200
201void QQuickFlipable::updatePolish()
202{
203 Q_D(QQuickFlipable);
204 d->updateSide();
205}
206
207/*! \internal
208 Flipable must use the complete scene transform to correctly determine the
209 currently visible side.
210
211 It must also be independent of camera distance, in case the contents are
212 too wide: for rotation transforms we simply call QMatrix4x4::rotate(),
213 whereas QQuickRotation::applyTo(QMatrix4x4*) calls
214 QMatrix4x4::projectedRotate() which by default assumes the camera distance
215 is 1024 virtual pixels. So for example if contents inside Flipable are to
216 be flipped around the y axis, and are wider than 1024*2, some of the
217 rendering goes behind the "camera". That's expected for rendering (since we
218 didn't provide API to change camera distance), but not ok for deciding when
219 to flip.
220*/
222{
223 Q_Q(QQuickFlipable);
224
225 if (!sideDirty)
226 return;
227
228 sideDirty = false;
229
230 QMatrix4x4 sceneTransform;
231
232 const qreal tx = x.value();
233 const qreal ty = y.value();
234 if (!qFuzzyIsNull(tx) || !qFuzzyIsNull(ty))
235 sceneTransform.translate(tx, ty);
236
237 for (const auto *transform : std::as_const(transforms)) {
238 if (const auto *rot = qobject_cast<const QQuickRotation *>(transform)) {
239 // rotation is a special case: we want to call rotate() instead of projectedRotate()
240 const auto angle = rot->angle();
241 const auto axis = rot->axis();
242 if (!(qFuzzyIsNull(angle) || axis.isNull())) {
243 sceneTransform.translate(rot->origin());
244 sceneTransform.rotate(angle, axis.x(), axis.y(), axis.z());
245 sceneTransform.translate(-rot->origin());
246 }
247 } else {
248 transform->applyTo(&sceneTransform);
249 }
250 }
251
252 const bool hasRotation = !qFuzzyIsNull(rotation());
253 const bool hasScale = !qFuzzyCompare(scale(), 1);
254 if (hasScale || hasRotation) {
255 QPointF tp = computeTransformOrigin();
256 sceneTransform.translate(tp.x(), tp.y());
257 if (hasScale)
258 sceneTransform.scale(scale(), scale());
259 if (hasRotation)
260 sceneTransform.rotate(rotation(), 0, 0, 1);
261 sceneTransform.translate(-tp.x(), -tp.y());
262 }
263
264 const QVector3D origin(sceneTransform.map(QPointF(0, 0)));
265 const QVector3D right = QVector3D(sceneTransform.map(QPointF(1, 0))) - origin;
266 const QVector3D top = QVector3D(sceneTransform.map(QPointF(0, 1))) - origin;
267
268 wantBackYFlipped = right.x() < 0;
269 wantBackXFlipped = top.y() < 0;
270
271 const QQuickFlipable::Side newSide =
272 QVector3D::crossProduct(top, right).z() > 0 ? QQuickFlipable::Back : QQuickFlipable::Front;
273
274 if (newSide != current) {
275 current = newSide;
276 if (current == QQuickFlipable::Back && back)
278 if (front) {
279 front->setOpacity((current==QQuickFlipable::Front)?1.:0.);
280 front->setEnabled((current==QQuickFlipable::Front)?true:false);
281 }
282 if (back) {
283 back->setOpacity((current==QQuickFlipable::Back)?1.:0.);
284 back->setEnabled((current==QQuickFlipable::Back)?true:false);
285 }
286 emit q->sideChanged();
287 }
288}
289
290/* Depends on the width/height of the back item, and so needs reevaulating
291 if those change.
292*/
294{
295 QTransform mat;
296 mat.translate(back->width()/2,back->height()/2);
297 if (back->width() && wantBackYFlipped)
298 mat.rotate(180, Qt::YAxis);
299 if (back->height() && wantBackXFlipped)
300 mat.rotate(180, Qt::XAxis);
301 mat.translate(-back->width()/2,-back->height()/2);
302
303 if (backTransform)
304 backTransform->setTransform(mat);
305}
306
307QT_END_NAMESPACE
308
309#include "qquickflipable.moc"
310#include "moc_qquickflipable_p.cpp"
QPointer< QQuickItem > back
QPointer< QQuickItem > front
QPointer< QQuickLocalTransform > backTransform