30c7a396c756c2488bd7395611d5867e7f5a976d
[profile/ivi/qtdeclarative.git] / src / qtquick1 / graphicsitems / qdeclarativerectangle.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
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 "QtQuick1/private/qdeclarativerectangle_p.h"
43 #include "QtQuick1/private/qdeclarativerectangle_p_p.h"
44
45 #include <QPainter>
46 #include <QStringBuilder>
47 #include <QtCore/qmath.h>
48 #include <QtWidgets/qdrawutil.h>
49
50 QT_BEGIN_NAMESPACE
51
52
53
54 /*!
55     \internal
56     \class QDeclarative1Pen
57     \brief The QDeclarative1Pen class provides a pen used for drawing rectangle borders on a QDeclarative1View.
58
59     By default, the pen is invalid and nothing is drawn. You must either set a color (then the default
60     width is 1) or a width (then the default color is black).
61
62     A width of 1 indicates is a single-pixel line on the border of the item being painted.
63
64     Example:
65     \qml
66     Rectangle {
67         border.width: 2
68         border.color: "red"
69     }
70     \endqml
71 */
72
73 void QDeclarative1Pen::setColor(const QColor &c)
74 {
75     _color = c;
76     _valid = (_color.alpha() && _width >= 1) ? true : false;
77     emit penChanged();
78 }
79
80 void QDeclarative1Pen::setWidth(int w)
81 {
82     if (_width == w && _valid)
83         return;
84
85     _width = w;
86     _valid = (_color.alpha() && _width >= 1) ? true : false;
87     emit penChanged();
88 }
89
90
91 /*!
92     \qmlclass GradientStop QDeclarative1GradientStop
93     \inqmlmodule QtQuick 1
94     \ingroup qml-basic-visual-elements
95     \since QtQuick 1.0
96     \brief The GradientStop item defines the color at a position in a Gradient.
97
98     \sa Gradient
99 */
100
101 /*!
102     \qmlproperty real QtQuick1::GradientStop::position
103     \qmlproperty color QtQuick1::GradientStop::color
104
105     The position and color properties describe the color used at a given
106     position in a gradient, as represented by a gradient stop.
107
108     The default position is 0.0; the default color is black.
109
110     \sa Gradient
111 */
112
113 void QDeclarative1GradientStop::updateGradient()
114 {
115     if (QDeclarative1Gradient *grad = qobject_cast<QDeclarative1Gradient*>(parent()))
116         grad->doUpdate();
117 }
118
119 /*!
120     \qmlclass Gradient QDeclarative1Gradient
121     \inqmlmodule QtQuick 1
122     \ingroup qml-basic-visual-elements
123     \since QtQuick 1.0
124     \brief The Gradient item defines a gradient fill.
125
126     A gradient is defined by two or more colors, which will be blended seamlessly.
127
128     The colors are specified as a set of GradientStop child items, each of
129     which defines a position on the gradient from 0.0 to 1.0 and a color.
130     The position of each GradientStop is defined by setting its
131     \l{GradientStop::}{position} property; its color is defined using its
132     \l{GradientStop::}{color} property.
133
134     A gradient without any gradient stops is rendered as a solid white fill.
135
136     Note that this item is not a visual representation of a gradient. To display a
137     gradient, use a visual element (like \l Rectangle) which supports the use
138     of gradients.
139
140     \section1 Example Usage
141
142     \div {class="float-right"}
143     \inlineimage qml-gradient.png
144     \enddiv
145
146     The following example declares a \l Rectangle item with a gradient starting
147     with red, blending to yellow at one third of the height of the rectangle,
148     and ending with green:
149
150     \snippet doc/src/snippets/qtquick1/gradient.qml code
151
152     \clearfloat
153     \section1 Performance and Limitations
154
155     Calculating gradients can be computationally expensive compared to the use
156     of solid color fills or images. Consider using gradients for static items
157     in a user interface.
158
159     In Qt 4.7, only vertical, linear gradients can be applied to items. If you
160     need to apply different orientations of gradients, a combination of rotation
161     and clipping will need to be applied to the relevant items. This can
162     introduce additional performance requirements for your application.
163
164     The use of animations involving gradient stops may not give the desired
165     result. An alternative way to animate gradients is to use pre-generated
166     images or SVG drawings containing gradients.
167
168     \sa GradientStop
169 */
170
171 /*!
172     \qmlproperty list<GradientStop> QtQuick1::Gradient::stops
173     \default
174
175     This property holds the gradient stops describing the gradient.
176
177     By default, this property contains an empty list.
178
179     To set the gradient stops, define them as children of the Gradient element.
180 */
181
182 const QGradient *QDeclarative1Gradient::gradient() const
183 {
184     if (!m_gradient && !m_stops.isEmpty()) {
185         m_gradient = new QLinearGradient(0,0,0,1.0);
186         for (int i = 0; i < m_stops.count(); ++i) {
187             const QDeclarative1GradientStop *stop = m_stops.at(i);
188             m_gradient->setCoordinateMode(QGradient::ObjectBoundingMode);
189             m_gradient->setColorAt(stop->position(), stop->color());
190         }
191     }
192
193     return m_gradient;
194 }
195
196 void QDeclarative1Gradient::doUpdate()
197 {
198     delete m_gradient;
199     m_gradient = 0;
200     emit updated();
201 }
202
203
204 /*!
205     \qmlclass Rectangle QDeclarative1Rectangle
206     \inqmlmodule QtQuick 1
207     \ingroup qml-basic-visual-elements
208     \since QtQuick 1.0
209     \brief The Rectangle item provides a filled rectangle with an optional border.
210     \inherits Item
211
212     Rectangle items are used to fill areas with solid color or gradients, and are
213     often used to hold other items.
214
215     \section1 Appearance
216
217     Each Rectangle item is painted using either a solid fill color, specified using
218     the \l color property, or a gradient, defined using a Gradient element and set
219     using the \l gradient property. If both a color and a gradient are specified,
220     the gradient is used.
221
222     You can add an optional border to a rectangle with its own color and thickness
223     by settting the \l border.color and \l border.width properties.
224
225     You can also create rounded rectangles using the \l radius property. Since this
226     introduces curved edges to the corners of a rectangle, it may be appropriate to
227     set the \l smooth property to improve its appearance.
228
229     \section1 Example Usage
230
231     \div {class="float-right"}
232     \inlineimage declarative-rect.png
233     \enddiv
234
235     The following example shows the effects of some of the common properties on a
236     Rectangle item, which in this case is used to create a square:
237
238     \snippet doc/src/snippets/qtquick1/rectangle/rectangle.qml document
239
240     \clearfloat
241     \section1 Performance
242
243     Using the \l smooth property improves the appearance of a rounded rectangle at
244     the cost of rendering performance. You should consider unsetting this property
245     for rectangles in motion, and only set it when they are stationary.
246
247     \sa Image
248 */
249
250 int QDeclarative1RectanglePrivate::doUpdateSlotIdx = -1;
251
252 QDeclarative1Rectangle::QDeclarative1Rectangle(QDeclarativeItem *parent)
253   : QDeclarativeItem(*(new QDeclarative1RectanglePrivate), parent)
254 {
255 }
256
257 void QDeclarative1Rectangle::doUpdate()
258 {
259     Q_D(QDeclarative1Rectangle);
260     d->rectImage = QPixmap();
261     const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0;
262     d->setPaintMargin((pw+1)/2);
263     update();
264 }
265
266 /*!
267     \qmlproperty int QtQuick1::Rectangle::border.width
268     \qmlproperty color QtQuick1::Rectangle::border.color
269
270     The width and color used to draw the border of the rectangle.
271
272     A width of 1 creates a thin line. For no line, use a width of 0 or a transparent color.
273
274     \note The width of the rectangle's border does not affect the geometry of the
275     rectangle itself or its position relative to other items if anchors are used.
276
277     If \c border.width is an odd number, the rectangle is painted at a half-pixel offset to retain
278     border smoothness. Also, the border is rendered evenly on either side of the
279     rectangle's boundaries, and the spare pixel is rendered to the right and below the
280     rectangle (as documented for QRect rendering). This can cause unintended effects if
281     \c border.width is 1 and the rectangle is \l{Item::clip}{clipped} by a parent item:
282
283     \div {class="float-right"}
284     \inlineimage rect-border-width.png
285     \enddiv
286
287     \snippet doc/src/snippets/qtquick1/rectangle/rect-border-width.qml 0
288
289     \clearfloat
290     Here, the innermost rectangle's border is clipped on the bottom and right edges by its
291     parent. To avoid this, the border width can be set to two instead of one.
292 */
293 QDeclarative1Pen *QDeclarative1Rectangle::border()
294 {
295     Q_D(QDeclarative1Rectangle);
296     return d->getPen();
297 }
298
299 /*!
300     \qmlproperty Gradient QtQuick1::Rectangle::gradient
301
302     The gradient to use to fill the rectangle.
303
304     This property allows for the construction of simple vertical gradients.
305     Other gradients may by formed by adding rotation to the rectangle.
306
307     \div {class="float-left"}
308     \inlineimage declarative-rect_gradient.png
309     \enddiv
310
311     \snippet doc/src/snippets/qtquick1/rectangle/rectangle-gradient.qml rectangles
312     \clearfloat
313
314     If both a gradient and a color are specified, the gradient will be used.
315
316     \sa Gradient, color
317 */
318 QDeclarative1Gradient *QDeclarative1Rectangle::gradient() const
319 {
320     Q_D(const QDeclarative1Rectangle);
321     return d->gradient;
322 }
323
324 void QDeclarative1Rectangle::setGradient(QDeclarative1Gradient *gradient)
325 {
326     Q_D(QDeclarative1Rectangle);
327     if (d->gradient == gradient)
328         return;
329     static int updatedSignalIdx = -1;
330     if (updatedSignalIdx < 0)
331         updatedSignalIdx = QDeclarative1Gradient::staticMetaObject.indexOfSignal("updated()");
332     if (d->doUpdateSlotIdx < 0)
333         d->doUpdateSlotIdx = QDeclarative1Rectangle::staticMetaObject.indexOfSlot("doUpdate()");
334     if (d->gradient)
335         QMetaObject::disconnect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
336     d->gradient = gradient;
337     if (d->gradient)
338         QMetaObject::connect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
339     update();
340 }
341
342
343 /*!
344     \qmlproperty real QtQuick1::Rectangle::radius
345     This property holds the corner radius used to draw a rounded rectangle.
346
347     If radius is non-zero, the rectangle will be painted as a rounded rectangle, otherwise it will be
348     painted as a normal rectangle. The same radius is used by all 4 corners; there is currently
349     no way to specify different radii for different corners.
350 */
351 qreal QDeclarative1Rectangle::radius() const
352 {
353     Q_D(const QDeclarative1Rectangle);
354     return d->radius;
355 }
356
357 void QDeclarative1Rectangle::setRadius(qreal radius)
358 {
359     Q_D(QDeclarative1Rectangle);
360     if (d->radius == radius)
361         return;
362
363     d->radius = radius;
364     d->rectImage = QPixmap();
365     update();
366     emit radiusChanged();
367 }
368
369 /*!
370     \qmlproperty color QtQuick1::Rectangle::color
371     This property holds the color used to fill the rectangle.
372
373     The default color is white.
374
375     \div {class="float-right"}
376     \inlineimage rect-color.png
377     \enddiv
378
379     The following example shows rectangles with colors specified
380     using hexadecimal and named color notation:
381
382     \snippet doc/src/snippets/qtquick1/rectangle/rectangle-colors.qml rectangles
383
384     \clearfloat
385     If both a gradient and a color are specified, the gradient will be used.
386
387     \sa gradient
388 */
389 QColor QDeclarative1Rectangle::color() const
390 {
391     Q_D(const QDeclarative1Rectangle);
392     return d->color;
393 }
394
395 void QDeclarative1Rectangle::setColor(const QColor &c)
396 {
397     Q_D(QDeclarative1Rectangle);
398     if (d->color == c)
399         return;
400
401     d->color = c;
402     d->rectImage = QPixmap();
403     update();
404     emit colorChanged();
405 }
406
407 void QDeclarative1Rectangle::generateRoundedRect()
408 {
409     Q_D(QDeclarative1Rectangle);
410     if (d->rectImage.isNull()) {
411         const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0;
412         const int radius = qCeil(d->radius);    //ensure odd numbered width/height so we get 1-pixel center
413
414         QString key = QLatin1String("q_") % QString::number(pw) % d->color.name() % QString::number(d->color.alpha(), 16) % QLatin1Char('_') % QString::number(radius);
415         if (d->pen && d->pen->isValid())
416             key += d->pen->color().name() % QString::number(d->pen->color().alpha(), 16);
417
418         if (!QPixmapCache::find(key, &d->rectImage)) {
419             d->rectImage = QPixmap(radius*2 + 3 + pw*2, radius*2 + 3 + pw*2);
420             d->rectImage.fill(Qt::transparent);
421             QPainter p(&(d->rectImage));
422             p.setRenderHint(QPainter::Antialiasing);
423             if (d->pen && d->pen->isValid()) {
424                 QPen pn(QColor(d->pen->color()), d->pen->width());
425                 p.setPen(pn);
426             } else {
427                 p.setPen(Qt::NoPen);
428             }
429             p.setBrush(d->color);
430             if (pw%2)
431                 p.drawRoundedRect(QRectF(qreal(pw)/2+1, qreal(pw)/2+1, d->rectImage.width()-(pw+1), d->rectImage.height()-(pw+1)), d->radius, d->radius);
432             else
433                 p.drawRoundedRect(QRectF(qreal(pw)/2, qreal(pw)/2, d->rectImage.width()-pw, d->rectImage.height()-pw), d->radius, d->radius);
434
435             // end painting before inserting pixmap
436             // to pixmap cache to avoid a deep copy
437             p.end();
438             QPixmapCache::insert(key, d->rectImage);
439         }
440     }
441 }
442
443 void QDeclarative1Rectangle::generateBorderedRect()
444 {
445     Q_D(QDeclarative1Rectangle);
446     if (d->rectImage.isNull()) {
447         const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0;
448
449         QString key = QLatin1String("q_") % QString::number(pw) % d->color.name() % QString::number(d->color.alpha(), 16);
450         if (d->pen && d->pen->isValid())
451             key += d->pen->color().name() % QString::number(d->pen->color().alpha(), 16);
452
453         if (!QPixmapCache::find(key, &d->rectImage)) {
454             // Adding 5 here makes qDrawBorderPixmap() paint correctly with smooth: true
455             // See QTBUG-7999 and QTBUG-10765 for more details.
456             d->rectImage = QPixmap(pw*2 + 5, pw*2 + 5);
457             d->rectImage.fill(Qt::transparent);
458             QPainter p(&(d->rectImage));
459             p.setRenderHint(QPainter::Antialiasing);
460             if (d->pen && d->pen->isValid()) {
461                 QPen pn(QColor(d->pen->color()), d->pen->width());
462                 pn.setJoinStyle(Qt::MiterJoin);
463                 p.setPen(pn);
464             } else {
465                 p.setPen(Qt::NoPen);
466             }
467             p.setBrush(d->color);
468             if (pw%2)
469                 p.drawRect(QRectF(qreal(pw)/2+1, qreal(pw)/2+1, d->rectImage.width()-(pw+1), d->rectImage.height()-(pw+1)));
470             else
471                 p.drawRect(QRectF(qreal(pw)/2, qreal(pw)/2, d->rectImage.width()-pw, d->rectImage.height()-pw));
472
473             // end painting before inserting pixmap
474             // to pixmap cache to avoid a deep copy
475             p.end();
476             QPixmapCache::insert(key, d->rectImage);
477         }
478     }
479 }
480
481 void QDeclarative1Rectangle::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
482 {
483     Q_D(QDeclarative1Rectangle);
484     if (width() <= 0 || height() <= 0)
485         return;
486     if (d->radius > 0 || (d->pen && d->pen->isValid())
487         || (d->gradient && d->gradient->gradient()) ) {
488         drawRect(*p);
489     }
490     else {
491         bool oldAA = p->testRenderHint(QPainter::Antialiasing);
492         if (d->smooth)
493             p->setRenderHints(QPainter::Antialiasing, true);
494         p->fillRect(QRectF(0, 0, width(), height()), d->color);
495         if (d->smooth)
496             p->setRenderHint(QPainter::Antialiasing, oldAA);
497     }
498 }
499
500 void QDeclarative1Rectangle::drawRect(QPainter &p)
501 {
502     Q_D(QDeclarative1Rectangle);
503     if ((d->gradient && d->gradient->gradient())
504         || d->radius > width()/2 || d->radius > height()/2
505         || width() < 3 || height() < 3) {
506         // XXX This path is still slower than the image path
507         // Image path won't work for gradients or invalid radius though
508         bool oldAA = p.testRenderHint(QPainter::Antialiasing);
509         if (d->smooth)
510             p.setRenderHint(QPainter::Antialiasing);
511         if (d->pen && d->pen->isValid()) {
512             QPen pn(QColor(d->pen->color()), d->pen->width());
513             pn.setJoinStyle(Qt::MiterJoin);
514             p.setPen(pn);
515         } else {
516             p.setPen(Qt::NoPen);
517         }
518         if (d->gradient && d->gradient->gradient())
519             p.setBrush(*d->gradient->gradient());
520         else
521             p.setBrush(d->color);
522         const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0;
523         QRectF rect;
524         if (pw%2)
525             rect = QRectF(0.5, 0.5, width()-1, height()-1);
526         else
527             rect = QRectF(0, 0, width(), height());
528         qreal radius = d->radius;
529         if (radius > width()/2 || radius > height()/2)
530             radius = qMin(width()/2, height()/2);
531         if (radius > 0.)
532             p.drawRoundedRect(rect, radius, radius);
533         else
534             p.drawRect(rect);
535         if (d->smooth)
536             p.setRenderHint(QPainter::Antialiasing, oldAA);
537     } else {
538         bool oldAA = p.testRenderHint(QPainter::Antialiasing);
539         bool oldSmooth = p.testRenderHint(QPainter::SmoothPixmapTransform);
540         if (d->smooth)
541             p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth);
542
543         const int pw = d->pen && d->pen->isValid() ? (d->pen->width()+1)/2*2 : 0;
544
545         if (d->radius > 0)
546             generateRoundedRect();
547         else
548             generateBorderedRect();
549
550         int xOffset = (d->rectImage.width()-1)/2;
551         int yOffset = (d->rectImage.height()-1)/2;
552         Q_ASSERT(d->rectImage.width() == 2*xOffset + 1);
553         Q_ASSERT(d->rectImage.height() == 2*yOffset + 1);
554
555         // check whether we've eliminated the center completely
556         if (2*xOffset > width()+pw)
557             xOffset = (width()+pw)/2;
558         if (2*yOffset > height()+pw)
559             yOffset = (height()+pw)/2;
560
561         QMargins margins(xOffset, yOffset, xOffset, yOffset);
562         QTileRules rules(Qt::StretchTile, Qt::StretchTile);
563         //NOTE: even though our item may have qreal-based width and height, qDrawBorderPixmap only supports QRects
564         qDrawBorderPixmap(&p, QRect(-pw/2, -pw/2, width()+pw, height()+pw), margins, d->rectImage, d->rectImage.rect(), margins, rules);
565
566         if (d->smooth) {
567             p.setRenderHint(QPainter::Antialiasing, oldAA);
568             p.setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth);
569         }
570     }
571 }
572
573 /*!
574     \qmlproperty bool QtQuick1::Rectangle::smooth
575
576     Set this property if you want the item to be smoothly scaled or
577     transformed.  Smooth filtering gives better visual quality, but is slower.  If
578     the item is displayed at its natural size, this property has no visual or
579     performance effect.
580
581     \note Generally scaling artifacts are only visible if the item is stationary on
582     the screen.  A common pattern when animating an item is to disable smooth
583     filtering at the beginning of the animation and reenable it at the conclusion.
584
585     \image rect-smooth.png
586     On this image, smooth is turned off on the top half and on on the bottom half.
587 */
588
589 QRectF QDeclarative1Rectangle::boundingRect() const
590 {
591     Q_D(const QDeclarative1Rectangle);
592     return QRectF(-d->paintmargin, -d->paintmargin, d->width()+d->paintmargin*2, d->height()+d->paintmargin*2);
593 }
594
595
596
597 QT_END_NAMESPACE