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