1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
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 https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qquickflipable_p.h"
41#include "qquickitem_p.h"
42
43
44#include <QtQml/qqmlinfo.h>
45
46QT_BEGIN_NAMESPACE
47
48// XXX todo - i think this needs work and a bit of a re-think
49
50class QQuickLocalTransform : public QQuickTransform
51{
52 Q_OBJECT
53public:
54 QQuickLocalTransform(QObject *parent) : QQuickTransform(parent) {}
55
56 void setTransform(const QTransform &t) {
57 transform = t;
58 update();
59 }
60 void applyTo(QMatrix4x4 *matrix) const override {
61 *matrix *= transform;
62 }
63private:
64 QTransform transform;
65};
66
67class QQuickFlipablePrivate : public QQuickItemPrivate
68{
69 Q_DECLARE_PUBLIC(QQuickFlipable)
70public:
71 QQuickFlipablePrivate() : current(QQuickFlipable::Front), front(nullptr), back(nullptr), sideDirty(false) {}
72
73 void transformChanged() override;
74 void updateSide();
75 void setBackTransform();
76
77 QQuickFlipable::Side current;
78 QPointer<QQuickLocalTransform> backTransform;
79 QPointer<QQuickItem> front;
80 QPointer<QQuickItem> back;
81
82 bool sideDirty;
83 bool wantBackXFlipped;
84 bool wantBackYFlipped;
85};
86
87/*!
88 \qmltype Flipable
89 \instantiates QQuickFlipable
90 \inqmlmodule QtQuick
91 \inherits Item
92 \ingroup qtquick-input
93 \ingroup qtquick-containers
94 \brief Provides a surface that can be flipped.
95
96 Flipable is an item that can be visibly "flipped" between its front and
97 back sides, like a card. It may used together with \l Rotation, \l State
98 and \l Transition types to produce a flipping effect.
99
100 The \l front and \l back properties are used to hold the items that are
101 shown respectively on the front and back sides of the flipable item.
102
103 \section1 Example Usage
104
105 The following example shows a Flipable item that flips whenever it is
106 clicked, rotating about the y-axis.
107
108 This flipable item has a \c flipped boolean property that is toggled
109 whenever the MouseArea within the flipable is clicked. When
110 \c flipped is true, the item changes to the "back" state; in this
111 state, the \c angle of the \l Rotation item is changed to 180
112 degrees to produce the flipping effect. When \c flipped is false, the
113 item reverts to the default state, in which the \c angle value is 0.
114
115 \snippet qml/flipable/flipable.qml 0
116
117 \image flipable.gif
118
119 The \l Transition creates the animation that changes the angle over
120 four seconds. When the item changes between its "back" and
121 default states, the NumberAnimation animates the angle between
122 its old and new values.
123
124 See \l {Qt Quick States} for details on state changes and the default
125 state, and \l {Animation and Transitions in Qt Quick} for more information on how
126 animations work within transitions.
127
128 \sa {customitems/flipable}{UI Components: Flipable Example}
129*/
130QQuickFlipable::QQuickFlipable(QQuickItem *parent)
131: QQuickItem(*(new QQuickFlipablePrivate), parent)
132{
133}
134
135QQuickFlipable::~QQuickFlipable()
136{
137}
138
139/*!
140 \qmlproperty Item QtQuick::Flipable::front
141 \qmlproperty Item QtQuick::Flipable::back
142
143 The front and back sides of the flipable.
144*/
145
146QQuickItem *QQuickFlipable::front() const
147{
148 Q_D(const QQuickFlipable);
149 return d->front;
150}
151
152void QQuickFlipable::setFront(QQuickItem *front)
153{
154 Q_D(QQuickFlipable);
155 if (d->front) {
156 qmlWarning(me: this) << tr(s: "front is a write-once property");
157 return;
158 }
159 d->front = front;
160 d->front->setParentItem(this);
161 if (Back == d->current) {
162 d->front->setOpacity(0.);
163 d->front->setEnabled(false);
164 }
165 emit frontChanged();
166}
167
168QQuickItem *QQuickFlipable::back()
169{
170 Q_D(const QQuickFlipable);
171 return d->back;
172}
173
174void QQuickFlipable::setBack(QQuickItem *back)
175{
176 Q_D(QQuickFlipable);
177 if (d->back) {
178 qmlWarning(me: this) << tr(s: "back is a write-once property");
179 return;
180 }
181 if (back == nullptr)
182 return;
183 d->back = back;
184 d->back->setParentItem(this);
185
186 d->backTransform = new QQuickLocalTransform(d->back);
187 d->backTransform->prependToItem(d->back);
188
189 if (Front == d->current) {
190 d->back->setOpacity(0.);
191 d->back->setEnabled(false);
192 }
193
194 connect(sender: back, SIGNAL(widthChanged()),
195 receiver: this, SLOT(retransformBack()));
196 connect(sender: back, SIGNAL(heightChanged()),
197 receiver: this, SLOT(retransformBack()));
198 emit backChanged();
199}
200
201void QQuickFlipable::retransformBack()
202{
203 Q_D(QQuickFlipable);
204 if (d->current == QQuickFlipable::Back && d->back)
205 d->setBackTransform();
206}
207
208/*!
209 \qmlproperty enumeration QtQuick::Flipable::side
210
211 The side of the Flipable currently visible. Possible values are \c
212 Flipable.Front and \c Flipable.Back.
213*/
214QQuickFlipable::Side QQuickFlipable::side() const
215{
216 Q_D(const QQuickFlipable);
217
218 const_cast<QQuickFlipablePrivate *>(d)->updateSide();
219 return d->current;
220}
221
222void QQuickFlipablePrivate::transformChanged()
223{
224 Q_Q(QQuickFlipable);
225
226 if (!sideDirty) {
227 sideDirty = true;
228 q->polish();
229 }
230
231 QQuickItemPrivate::transformChanged();
232}
233
234void QQuickFlipable::updatePolish()
235{
236 Q_D(QQuickFlipable);
237 d->updateSide();
238}
239
240// determination on the currently visible side of the flipable
241// has to be done on the complete scene transform to give
242// correct results.
243void QQuickFlipablePrivate::updateSide()
244{
245 Q_Q(QQuickFlipable);
246
247 if (!sideDirty)
248 return;
249
250 sideDirty = false;
251
252 QTransform sceneTransform;
253 itemToParentTransform(sceneTransform);
254
255 QPointF p1(0, 0);
256 QPointF p2(1, 0);
257 QPointF p3(1, 1);
258
259 QPointF scenep1 = sceneTransform.map(p: p1);
260 QPointF scenep2 = sceneTransform.map(p: p2);
261 QPointF scenep3 = sceneTransform.map(p: p3);
262#if 0
263 p1 = q->mapToParent(p1);
264 p2 = q->mapToParent(p2);
265 p3 = q->mapToParent(p3);
266#endif
267
268 qreal cross = (scenep1.x() - scenep2.x()) * (scenep3.y() - scenep2.y()) -
269 (scenep1.y() - scenep2.y()) * (scenep3.x() - scenep2.x());
270
271 wantBackYFlipped = scenep1.x() >= scenep2.x();
272 wantBackXFlipped = scenep2.y() >= scenep3.y();
273
274 QQuickFlipable::Side newSide;
275 if (cross > 0) {
276 newSide = QQuickFlipable::Back;
277 } else {
278 newSide = QQuickFlipable::Front;
279 }
280
281 if (newSide != current) {
282 current = newSide;
283 if (current == QQuickFlipable::Back && back)
284 setBackTransform();
285 if (front) {
286 front->setOpacity((current==QQuickFlipable::Front)?1.:0.);
287 front->setEnabled((current==QQuickFlipable::Front)?true:false);
288 }
289 if (back) {
290 back->setOpacity((current==QQuickFlipable::Back)?1.:0.);
291 back->setEnabled((current==QQuickFlipable::Back)?true:false);
292 }
293 emit q->sideChanged();
294 }
295}
296
297/* Depends on the width/height of the back item, and so needs reevaulating
298 if those change.
299*/
300void QQuickFlipablePrivate::setBackTransform()
301{
302 QTransform mat;
303 mat.translate(dx: back->width()/2,dy: back->height()/2);
304 if (back->width() && wantBackYFlipped)
305 mat.rotate(a: 180, axis: Qt::YAxis);
306 if (back->height() && wantBackXFlipped)
307 mat.rotate(a: 180, axis: Qt::XAxis);
308 mat.translate(dx: -back->width()/2,dy: -back->height()/2);
309
310 if (backTransform)
311 backTransform->setTransform(mat);
312}
313
314QT_END_NAMESPACE
315
316#include "qquickflipable.moc"
317#include "moc_qquickflipable_p.cpp"
318

source code of qtdeclarative/src/quick/items/qquickflipable.cpp