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 {Qt logo flipping between front and back sides
85 with rotation animation}
86
87 The \l Transition creates the animation that changes the angle over
88 four seconds. When the item changes between its "back" and
89 default states, the NumberAnimation animates the angle between
90 its old and new values.
91
92 See \l {Qt Quick States} for details on state changes and the default
93 state, and \l {Animation and Transitions in Qt Quick} for more information on how
94 animations work within transitions.
95
96 \sa {customitems/flipable}{UI Components: Flipable Example}
97*/
98QQuickFlipable::QQuickFlipable(QQuickItem *parent)
99: QQuickItem(*(new QQuickFlipablePrivate), parent)
100{
101}
102
103QQuickFlipable::~QQuickFlipable()
104{
105}
106
107/*!
108 \qmlproperty Item QtQuick::Flipable::front
109 \qmlproperty Item QtQuick::Flipable::back
110
111 The front and back sides of the flipable.
112*/
113
114QQuickItem *QQuickFlipable::front() const
115{
116 Q_D(const QQuickFlipable);
117 return d->front;
118}
119
120void QQuickFlipable::setFront(QQuickItem *front)
121{
122 Q_D(QQuickFlipable);
123 if (d->front) {
124 qmlWarning(this) << tr("front is a write-once property");
125 return;
126 }
127 d->front = front;
128 d->front->setParentItem(this);
129 if (Back == d->current) {
130 d->front->setOpacity(0.);
131 d->front->setEnabled(false);
132 }
133 emit frontChanged();
134}
135
136QQuickItem *QQuickFlipable::back()
137{
138 Q_D(const QQuickFlipable);
139 return d->back;
140}
141
142void QQuickFlipable::setBack(QQuickItem *back)
143{
144 Q_D(QQuickFlipable);
145 if (d->back) {
146 qmlWarning(this) << tr("back is a write-once property");
147 return;
148 }
149 if (back == nullptr)
150 return;
151 d->back = back;
152 d->back->setParentItem(this);
153
154 d->backTransform = new QQuickLocalTransform(d->back);
155 d->backTransform->prependToItem(d->back);
156
157 if (Front == d->current) {
158 d->back->setOpacity(0.);
159 d->back->setEnabled(false);
160 }
161
162 connect(back, SIGNAL(widthChanged()),
163 this, SLOT(retransformBack()));
164 connect(back, SIGNAL(heightChanged()),
165 this, SLOT(retransformBack()));
166 emit backChanged();
167}
168
169void QQuickFlipable::retransformBack()
170{
171 Q_D(QQuickFlipable);
172 if (d->current == QQuickFlipable::Back && d->back)
173 d->setBackTransform();
174}
175
176/*!
177 \qmlproperty enumeration QtQuick::Flipable::side
178
179 The side of the Flipable currently visible. Possible values are \c
180 Flipable.Front and \c Flipable.Back.
181*/
182QQuickFlipable::Side QQuickFlipable::side() const
183{
184 Q_D(const QQuickFlipable);
185
186 const_cast<QQuickFlipablePrivate *>(d)->updateSide();
187 return d->current;
188}
189
190bool QQuickFlipablePrivate::transformChanged(QQuickItem *transformedItem)
191{
192 Q_Q(QQuickFlipable);
193
194 if (!sideDirty) {
195 sideDirty = true;
196 q->polish();
197 }
198
199 return QQuickItemPrivate::transformChanged(transformedItem);
200}
201
202void QQuickFlipable::updatePolish()
203{
204 Q_D(QQuickFlipable);
205 d->updateSide();
206}
207
208/*! \internal
209 Flipable must use the complete scene transform to correctly determine the
210 currently visible side.
211
212 It must also be independent of camera distance, in case the contents are
213 too wide: for rotation transforms we simply call QMatrix4x4::rotate(),
214 whereas QQuickRotation::applyTo(QMatrix4x4*) calls
215 QMatrix4x4::projectedRotate() which by default assumes the camera distance
216 is 1024 virtual pixels. So for example if contents inside Flipable are to
217 be flipped around the y axis, and are wider than 1024*2, some of the
218 rendering goes behind the "camera". That's expected for rendering (since we
219 didn't provide API to change camera distance), but not ok for deciding when
220 to flip.
221*/
223{
224 Q_Q(QQuickFlipable);
225
226 if (!sideDirty)
227 return;
228
229 sideDirty = false;
230
231 QMatrix4x4 sceneTransform;
232
233 const qreal tx = x.value();
234 const qreal ty = y.value();
235 if (!qFuzzyIsNull(tx) || !qFuzzyIsNull(ty))
236 sceneTransform.translate(tx, ty);
237
238 for (const auto *transform : std::as_const(transforms)) {
239 if (const auto *rot = qobject_cast<const QQuickRotation *>(transform)) {
240 // rotation is a special case: we want to call rotate() instead of projectedRotate()
241 const auto angle = rot->angle();
242 const auto axis = rot->axis();
243 if (!(qFuzzyIsNull(angle) || axis.isNull())) {
244 sceneTransform.translate(rot->origin());
245 sceneTransform.rotate(angle, axis.x(), axis.y(), axis.z());
246 sceneTransform.translate(-rot->origin());
247 }
248 } else {
249 transform->applyTo(&sceneTransform);
250 }
251 }
252
253 const bool hasRotation = !qFuzzyIsNull(rotation());
254 const bool hasScale = !qFuzzyCompare(scale(), 1);
255 if (hasScale || hasRotation) {
256 QPointF tp = computeTransformOrigin();
257 sceneTransform.translate(tp.x(), tp.y());
258 if (hasScale)
259 sceneTransform.scale(scale(), scale());
260 if (hasRotation)
261 sceneTransform.rotate(rotation(), 0, 0, 1);
262 sceneTransform.translate(-tp.x(), -tp.y());
263 }
264
265 const QVector3D origin(sceneTransform.map(QPointF(0, 0)));
266 const QVector3D right = QVector3D(sceneTransform.map(QPointF(1, 0))) - origin;
267 const QVector3D top = QVector3D(sceneTransform.map(QPointF(0, 1))) - origin;
268
269 wantBackYFlipped = right.x() < 0;
270 wantBackXFlipped = top.y() < 0;
271
272 const QQuickFlipable::Side newSide =
273 QVector3D::crossProduct(top, right).z() > 0 ? QQuickFlipable::Back : QQuickFlipable::Front;
274
275 if (newSide != current) {
276 current = newSide;
277 if (current == QQuickFlipable::Back && back)
279 if (front) {
280 front->setOpacity((current==QQuickFlipable::Front)?1.:0.);
281 front->setEnabled((current==QQuickFlipable::Front)?true:false);
282 }
283 if (back) {
284 back->setOpacity((current==QQuickFlipable::Back)?1.:0.);
285 back->setEnabled((current==QQuickFlipable::Back)?true:false);
286 }
287 emit q->sideChanged();
288 }
289}
290
291/* Depends on the width/height of the back item, and so needs reevaulating
292 if those change.
293*/
295{
296 QTransform mat;
297 mat.translate(back->width()/2,back->height()/2);
298 if (back->width() && wantBackYFlipped)
299 mat.rotate(180, Qt::YAxis);
300 if (back->height() && wantBackXFlipped)
301 mat.rotate(180, Qt::XAxis);
302 mat.translate(-back->width()/2,-back->height()/2);
303
304 if (backTransform)
305 backTransform->setTransform(mat);
306}
307
308QT_END_NAMESPACE
309
310#include "qquickflipable.moc"
311#include "moc_qquickflipable_p.cpp"
QPointer< QQuickItem > back
QPointer< QQuickItem > front
QPointer< QQuickLocalTransform > backTransform
Combined button and popup list for selecting options.