Doc: Sanitized QML types
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickborderimage.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickborderimage_p.h"
43 #include "qquickborderimage_p_p.h"
44 #include "qquickninepatchnode_p.h"
45
46 #include <QtQml/qqmlinfo.h>
47 #include <QtQml/qqmlfile.h>
48 #include <QtQml/qqmlengine.h>
49 #include <QtNetwork/qnetworkreply.h>
50 #include <QtCore/qfile.h>
51
52 #include <private/qqmlglobal_p.h>
53
54 QT_BEGIN_NAMESPACE
55
56
57 /*!
58     \qmlclass BorderImage QQuickBorderImage
59     \inqmlmodule QtQuick 2
60     \brief For specifying an image that can be used as a border
61     \inherits Item
62     \ingroup qml-basic-visual-elements
63
64     The BorderImage element is used to create borders out of images by scaling or tiling
65     parts of each image.
66
67     A BorderImage element breaks a source image, specified using the \l url property,
68     into 9 regions, as shown below:
69
70     \image declarative-scalegrid.png
71
72     When the image is scaled, regions of the source image are scaled or tiled to
73     create the displayed border image in the following way:
74
75     \list
76     \li The corners (regions 1, 3, 7, and 9) are not scaled at all.
77     \li Regions 2 and 8 are scaled according to
78        \l{BorderImage::horizontalTileMode}{horizontalTileMode}.
79     \li Regions 4 and 6 are scaled according to
80        \l{BorderImage::verticalTileMode}{verticalTileMode}.
81     \li The middle (region 5) is scaled according to both
82        \l{BorderImage::horizontalTileMode}{horizontalTileMode} and
83        \l{BorderImage::verticalTileMode}{verticalTileMode}.
84     \endlist
85
86     The regions of the image are defined using the \l border property group, which
87     describes the distance from each edge of the source image to use as a border.
88
89     \section1 Example Usage
90
91     The following examples show the effects of the different modes on an image.
92     Guide lines are overlaid onto the image to show the different regions of the
93     image as described above.
94
95     \beginfloatleft
96     \image qml-borderimage-normal-image.png
97     \endfloat
98
99     An unscaled image is displayed using an Image element. The \l border property is
100     used to determine the parts of the image that will lie inside the unscaled corner
101     areas and the parts that will be stretched horizontally and vertically.
102
103     \snippet doc/snippets/qml/borderimage/normal-image.qml normal image
104
105     \clearfloat
106     \beginfloatleft
107     \image qml-borderimage-scaled.png
108     \endfloat
109
110     A BorderImage element is used to display the image, and it is given a size that is
111     larger than the original image. Since the \l horizontalTileMode property is set to
112     \l{BorderImage::horizontalTileMode}{BorderImage.Stretch}, the parts of image in
113     regions 2 and 8 are stretched horizontally. Since the \l verticalTileMode property
114     is set to \l{BorderImage::verticalTileMode}{BorderImage.Stretch}, the parts of image
115     in regions 4 and 6 are stretched vertically.
116
117     \snippet doc/snippets/qml/borderimage/borderimage-scaled.qml scaled border image
118
119     \clearfloat
120     \beginfloatleft
121     \image qml-borderimage-tiled.png
122     \endfloat
123
124     Again, a large BorderImage element is used to display the image. With the
125     \l horizontalTileMode property set to \l{BorderImage::horizontalTileMode}{BorderImage.Repeat},
126     the parts of image in regions 2 and 8 are tiled so that they fill the space at the
127     top and bottom of the element. Similarly, the \l verticalTileMode property is set to
128     \l{BorderImage::verticalTileMode}{BorderImage.Repeat}, the parts of image in regions
129     4 and 6 are tiled so that they fill the space at the left and right of the element.
130
131     \snippet doc/snippets/qml/borderimage/borderimage-tiled.qml tiled border image
132
133     \clearfloat
134     In some situations, the width of regions 2 and 8 may not be an exact multiple of the width
135     of the corresponding regions in the source image. Similarly, the height of regions 4 and 6
136     may not be an exact multiple of the height of the corresponding regions. It can be useful
137     to use \l{BorderImage::horizontalTileMode}{BorderImage.Round} instead of
138     \l{BorderImage::horizontalTileMode}{BorderImage.Repeat} in cases like these.
139
140     The \l{declarative/imageelements/borderimage}{BorderImage example} shows how a BorderImage
141     can be used to simulate a shadow effect on a rectangular item.
142
143     \section1 Quality and Performance
144
145     By default, any scaled regions of the image are rendered without smoothing to improve
146     rendering speed. Setting the \l smooth property improves rendering quality of scaled
147     regions, but may slow down rendering.
148
149     The source image may not be loaded instantaneously, depending on its original location.
150     Loading progress can be monitored with the \l progress property.
151
152     \sa Image, AnimatedImage
153  */
154
155 /*!
156     \qmlproperty bool QtQuick2::BorderImage::asynchronous
157
158     Specifies that images on the local filesystem should be loaded
159     asynchronously in a separate thread.  The default value is
160     false, causing the user interface thread to block while the
161     image is loaded.  Setting \a asynchronous to true is useful where
162     maintaining a responsive user interface is more desirable
163     than having images immediately visible.
164
165     Note that this property is only valid for images read from the
166     local filesystem.  Images loaded via a network resource (e.g. HTTP)
167     are always loaded asynchronously.
168 */
169 QQuickBorderImage::QQuickBorderImage(QQuickItem *parent)
170 : QQuickImageBase(*(new QQuickBorderImagePrivate), parent)
171 {
172 }
173
174 QQuickBorderImage::~QQuickBorderImage()
175 {
176     Q_D(QQuickBorderImage);
177     if (d->sciReply)
178         d->sciReply->deleteLater();
179 }
180
181 /*!
182     \qmlproperty enumeration QtQuick2::BorderImage::status
183
184     This property describes the status of image loading.  It can be one of:
185
186     \list
187     \li BorderImage.Null - no image has been set
188     \li BorderImage.Ready - the image has been loaded
189     \li BorderImage.Loading - the image is currently being loaded
190     \li BorderImage.Error - an error occurred while loading the image
191     \endlist
192
193     \sa progress
194 */
195
196 /*!
197     \qmlproperty real QtQuick2::BorderImage::progress
198
199     This property holds the progress of image loading, from 0.0 (nothing loaded)
200     to 1.0 (finished).
201
202     \sa status
203 */
204
205 /*!
206     \qmlproperty bool QtQuick2::BorderImage::smooth
207
208     Set this property if you want the image to be smoothly filtered when scaled or
209     transformed.  Smooth filtering gives better visual quality, but is slower.  If
210     the image is displayed at its natural size, this property has no visual or
211     performance effect.
212
213     By default, this property is set to false.
214
215     \note Generally scaling artifacts are only visible if the image is stationary on
216     the screen.  A common pattern when animating an image is to disable smooth
217     filtering at the beginning of the animation and enable it at the conclusion.
218 */
219
220 /*!
221     \qmlproperty bool QtQuick2::BorderImage::cache
222
223     Specifies whether the image should be cached. The default value is
224     true. Setting \a cache to false is useful when dealing with large images,
225     to make sure that they aren't cached at the expense of small 'ui element' images.
226 */
227
228 /*!
229     \qmlproperty bool QtQuick2::BorderImage::mirror
230
231     This property holds whether the image should be horizontally inverted
232     (effectively displaying a mirrored image).
233
234     The default value is false.
235 */
236
237 /*!
238     \qmlproperty url QtQuick2::BorderImage::source
239
240     This property holds the URL that refers to the source image.
241
242     BorderImage can handle any image format supported by Qt, loaded from any
243     URL scheme supported by Qt.
244
245     This property can also be used to refer to .sci files, which are
246     written in a QML-specific, text-based format that specifies the
247     borders, the image file and the tile rules for a given border image.
248
249     The following .sci file sets the borders to 10 on each side for the
250     image \c picture.png:
251
252     \code
253     border.left: 10
254     border.top: 10
255     border.bottom: 10
256     border.right: 10
257     source: "picture.png"
258     \endcode
259
260     The URL may be absolute, or relative to the URL of the component.
261
262     \sa QQuickImageProvider
263 */
264
265 /*!
266     \qmlproperty QSize QtQuick2::BorderImage::sourceSize
267
268     This property holds the actual width and height of the loaded image.
269
270     In BorderImage, this property is read-only.
271
272     \sa Image::sourceSize
273 */
274 void QQuickBorderImage::setSource(const QUrl &url)
275 {
276     Q_D(QQuickBorderImage);
277     //equality is fairly expensive, so we bypass for simple, common case
278     if ((d->url.isEmpty() == url.isEmpty()) && url == d->url)
279         return;
280
281     if (d->sciReply) {
282         d->sciReply->deleteLater();
283         d->sciReply = 0;
284     }
285
286     d->url = url;
287     d->sciurl = QUrl();
288     emit sourceChanged(d->url);
289
290     if (isComponentComplete())
291         load();
292 }
293
294 void QQuickBorderImage::load()
295 {
296     Q_D(QQuickBorderImage);
297     if (d->progress != 0.0) {
298         d->progress = 0.0;
299         emit progressChanged(d->progress);
300     }
301
302     if (d->url.isEmpty()) {
303         d->pix.clear(this);
304         d->status = Null;
305         setImplicitSize(0, 0);
306         emit statusChanged(d->status);
307         update();
308     } else {
309         d->status = Loading;
310         if (d->url.path().endsWith(QLatin1String("sci"))) {
311             QString lf = QQmlFile::urlToLocalFileOrQrc(d->url);
312             if (!lf.isEmpty()) {
313                 QFile file(lf);
314                 file.open(QIODevice::ReadOnly);
315                 setGridScaledImage(QQuickGridScaledImage(&file));
316             } else {
317                 QNetworkRequest req(d->url);
318                 d->sciReply = qmlEngine(this)->networkAccessManager()->get(req);
319                 qmlobject_connect(d->sciReply, QNetworkReply, SIGNAL(finished()),
320                                   this, QQuickBorderImage, SLOT(sciRequestFinished()))
321             }
322         } else {
323
324             QQuickPixmap::Options options;
325             if (d->async)
326                 options |= QQuickPixmap::Asynchronous;
327             if (d->cache)
328                 options |= QQuickPixmap::Cache;
329             d->pix.clear(this);
330             d->pix.load(qmlEngine(this), d->url, options);
331
332             if (d->pix.isLoading()) {
333                 d->pix.connectFinished(this, SLOT(requestFinished()));
334                 d->pix.connectDownloadProgress(this, SLOT(requestProgress(qint64,qint64)));
335             } else {
336                 QSize impsize = d->pix.implicitSize();
337                 setImplicitSize(impsize.width(), impsize.height());
338
339                 if (d->pix.isReady()) {
340                     d->status = Ready;
341                 } else {
342                     d->status = Error;
343                     qmlInfo(this) << d->pix.error();
344                 }
345
346                 d->progress = 1.0;
347                 emit statusChanged(d->status);
348                 emit progressChanged(d->progress);
349                 update();
350             }
351         }
352     }
353
354     emit statusChanged(d->status);
355 }
356
357 /*!
358     \qmlproperty int QtQuick2::BorderImage::border.left
359     \qmlproperty int QtQuick2::BorderImage::border.right
360     \qmlproperty int QtQuick2::BorderImage::border.top
361     \qmlproperty int QtQuick2::BorderImage::border.bottom
362
363     The 4 border lines (2 horizontal and 2 vertical) break the image into 9 sections,
364     as shown below:
365
366     \image declarative-scalegrid.png
367
368     Each border line (left, right, top, and bottom) specifies an offset in pixels
369     from the respective edge of the source image. By default, each border line has
370     a value of 0.
371
372     For example, the following definition sets the bottom line 10 pixels up from
373     the bottom of the image:
374
375     \qml
376     BorderImage {
377         border.bottom: 10
378         // ...
379     }
380     \endqml
381
382     The border lines can also be specified using a
383     \l {BorderImage::source}{.sci file}.
384 */
385
386 QQuickScaleGrid *QQuickBorderImage::border()
387 {
388     Q_D(QQuickBorderImage);
389     return d->getScaleGrid();
390 }
391
392 /*!
393     \qmlproperty enumeration QtQuick2::BorderImage::horizontalTileMode
394     \qmlproperty enumeration QtQuick2::BorderImage::verticalTileMode
395
396     This property describes how to repeat or stretch the middle parts of the border image.
397
398     \list
399     \li BorderImage.Stretch - Scales the image to fit to the available area.
400     \li BorderImage.Repeat - Tile the image until there is no more space. May crop the last image.
401     \li BorderImage.Round - Like Repeat, but scales the images down to ensure that the last image is not cropped.
402     \endlist
403
404     The default tile mode for each property is BorderImage.Stretch.
405 */
406 QQuickBorderImage::TileMode QQuickBorderImage::horizontalTileMode() const
407 {
408     Q_D(const QQuickBorderImage);
409     return d->horizontalTileMode;
410 }
411
412 void QQuickBorderImage::setHorizontalTileMode(TileMode t)
413 {
414     Q_D(QQuickBorderImage);
415     if (t != d->horizontalTileMode) {
416         d->horizontalTileMode = t;
417         emit horizontalTileModeChanged();
418         update();
419     }
420 }
421
422 QQuickBorderImage::TileMode QQuickBorderImage::verticalTileMode() const
423 {
424     Q_D(const QQuickBorderImage);
425     return d->verticalTileMode;
426 }
427
428 void QQuickBorderImage::setVerticalTileMode(TileMode t)
429 {
430     Q_D(QQuickBorderImage);
431     if (t != d->verticalTileMode) {
432         d->verticalTileMode = t;
433         emit verticalTileModeChanged();
434         update();
435     }
436 }
437
438 void QQuickBorderImage::setGridScaledImage(const QQuickGridScaledImage& sci)
439 {
440     Q_D(QQuickBorderImage);
441     if (!sci.isValid()) {
442         d->status = Error;
443         emit statusChanged(d->status);
444     } else {
445         QQuickScaleGrid *sg = border();
446         sg->setTop(sci.gridTop());
447         sg->setBottom(sci.gridBottom());
448         sg->setLeft(sci.gridLeft());
449         sg->setRight(sci.gridRight());
450         d->horizontalTileMode = sci.horizontalTileRule();
451         d->verticalTileMode = sci.verticalTileRule();
452
453         d->sciurl = d->url.resolved(QUrl(sci.pixmapUrl()));
454
455         QQuickPixmap::Options options;
456         if (d->async)
457             options |= QQuickPixmap::Asynchronous;
458         if (d->cache)
459             options |= QQuickPixmap::Cache;
460         d->pix.clear(this);
461         d->pix.load(qmlEngine(this), d->sciurl, options);
462
463         if (d->pix.isLoading()) {
464             static int thisRequestProgress = -1;
465             static int thisRequestFinished = -1;
466             if (thisRequestProgress == -1) {
467                 thisRequestProgress =
468                     QQuickBorderImage::staticMetaObject.indexOfSlot("requestProgress(qint64,qint64)");
469                 thisRequestFinished =
470                     QQuickBorderImage::staticMetaObject.indexOfSlot("requestFinished()");
471             }
472
473             d->pix.connectFinished(this, thisRequestFinished);
474             d->pix.connectDownloadProgress(this, thisRequestProgress);
475
476         } else {
477
478             QSize impsize = d->pix.implicitSize();
479             setImplicitSize(impsize.width(), impsize.height());
480
481             if (d->pix.isReady()) {
482                 d->status = Ready;
483             } else {
484                 d->status = Error;
485                 qmlInfo(this) << d->pix.error();
486             }
487
488             d->progress = 1.0;
489             emit statusChanged(d->status);
490             emit progressChanged(1.0);
491             update();
492
493         }
494     }
495 }
496
497 void QQuickBorderImage::requestFinished()
498 {
499     Q_D(QQuickBorderImage);
500
501     QSize impsize = d->pix.implicitSize();
502     if (d->pix.isError()) {
503         d->status = Error;
504         qmlInfo(this) << d->pix.error();
505     } else {
506         d->status = Ready;
507     }
508
509     setImplicitSize(impsize.width(), impsize.height());
510
511     if (d->sourcesize.width() != d->pix.width() || d->sourcesize.height() != d->pix.height())
512         emit sourceSizeChanged();
513
514     d->progress = 1.0;
515     emit statusChanged(d->status);
516     emit progressChanged(1.0);
517     update();
518 }
519
520 #define BORDERIMAGE_MAX_REDIRECT 16
521
522 void QQuickBorderImage::sciRequestFinished()
523 {
524     Q_D(QQuickBorderImage);
525
526     d->redirectCount++;
527     if (d->redirectCount < BORDERIMAGE_MAX_REDIRECT) {
528         QVariant redirect = d->sciReply->attribute(QNetworkRequest::RedirectionTargetAttribute);
529         if (redirect.isValid()) {
530             QUrl url = d->sciReply->url().resolved(redirect.toUrl());
531             setSource(url);
532             return;
533         }
534     }
535     d->redirectCount=0;
536
537     if (d->sciReply->error() != QNetworkReply::NoError) {
538         d->status = Error;
539         d->sciReply->deleteLater();
540         d->sciReply = 0;
541         emit statusChanged(d->status);
542     } else {
543         QQuickGridScaledImage sci(d->sciReply);
544         d->sciReply->deleteLater();
545         d->sciReply = 0;
546         setGridScaledImage(sci);
547     }
548 }
549
550 void QQuickBorderImage::doUpdate()
551 {
552     update();
553 }
554
555 QSGNode *QQuickBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
556 {
557     Q_D(QQuickBorderImage);
558
559     QSGTexture *texture = d->sceneGraphContext()->textureForFactory(d->pix.textureFactory(), canvas());
560
561     if (!texture || width() <= 0 || height() <= 0) {
562         delete oldNode;
563         return 0;
564     }
565
566     QQuickNinePatchNode *node = static_cast<QQuickNinePatchNode *>(oldNode);
567
568     if (!node) {
569         node = new QQuickNinePatchNode();
570     }
571
572     node->setTexture(texture);
573
574     // Don't implicitly create the scalegrid in the rendering thread...
575     if (d->border) {
576         const QQuickScaleGrid *border = d->getScaleGrid();
577         node->setInnerRect(QRectF(border->left(),
578                                   border->top(),
579                                   qMax(1, d->pix.width() - border->right() - border->left()),
580                                   qMax(1, d->pix.height() - border->bottom() - border->top())));
581     } else {
582         node->setInnerRect(QRectF(0, 0, d->pix.width(), d->pix.height()));
583     }
584     node->setRect(QRectF(0, 0, width(), height()));
585     node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
586     node->setHorzontalTileMode(d->horizontalTileMode);
587     node->setVerticalTileMode(d->verticalTileMode);
588     node->setMirror(d->mirror);
589     node->update();
590
591     return node;
592 }
593
594 void QQuickBorderImage::pixmapChange()
595 {
596     Q_D(QQuickBorderImage);
597
598     d->pixmapChanged = true;
599
600     // When the pixmap changes, such as being deleted, we need to update the textures
601     update();
602 }
603
604 QT_END_NAMESPACE