1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "private/qdeclarativerectangle_p.h"
43 #include "private/qdeclarativerectangle_p_p.h"
46 #include <QStringBuilder>
47 #include <QtCore/qmath.h>
53 \class QDeclarativePen
54 \brief The QDeclarativePen class provides a pen used for drawing rectangle borders on a QDeclarativeView.
56 By default, the pen is invalid and nothing is drawn. You must either set a color (then the default
57 width is 1) or a width (then the default color is black).
59 A width of 1 indicates is a single-pixel line on the border of the item being painted.
70 void QDeclarativePen::setColor(const QColor &c)
73 _valid = (_color.alpha() && _width >= 1) ? true : false;
77 void QDeclarativePen::setWidth(int w)
79 if (_width == w && _valid)
83 _valid = (_color.alpha() && _width >= 1) ? true : false;
89 \qmlclass GradientStop QDeclarativeGradientStop
90 \ingroup qml-basic-visual-elements
92 \brief The GradientStop item defines the color at a position in a Gradient.
98 \qmlproperty real GradientStop::position
99 \qmlproperty color GradientStop::color
101 The position and color properties describe the color used at a given
102 position in a gradient, as represented by a gradient stop.
104 The default position is 0.0; the default color is black.
109 void QDeclarativeGradientStop::updateGradient()
111 if (QDeclarativeGradient *grad = qobject_cast<QDeclarativeGradient*>(parent()))
116 \qmlclass Gradient QDeclarativeGradient
117 \ingroup qml-basic-visual-elements
119 \brief The Gradient item defines a gradient fill.
121 A gradient is defined by two or more colors, which will be blended seamlessly.
123 The colors are specified as a set of GradientStop child items, each of
124 which defines a position on the gradient from 0.0 to 1.0 and a color.
125 The position of each GradientStop is defined by setting its
126 \l{GradientStop::}{position} property; its color is defined using its
127 \l{GradientStop::}{color} property.
129 A gradient without any gradient stops is rendered as a solid white fill.
131 Note that this item is not a visual representation of a gradient. To display a
132 gradient, use a visual element (like \l Rectangle) which supports the use
135 \section1 Example Usage
137 \div {class="float-right"}
138 \inlineimage qml-gradient.png
141 The following example declares a \l Rectangle item with a gradient starting
142 with red, blending to yellow at one third of the height of the rectangle,
143 and ending with green:
145 \snippet doc/src/snippets/declarative/gradient.qml code
148 \section1 Performance and Limitations
150 Calculating gradients can be computationally expensive compared to the use
151 of solid color fills or images. Consider using gradients for static items
154 In Qt 4.7, only vertical, linear gradients can be applied to items. If you
155 need to apply different orientations of gradients, a combination of rotation
156 and clipping will need to be applied to the relevant items. This can
157 introduce additional performance requirements for your application.
159 The use of animations involving gradient stops may not give the desired
160 result. An alternative way to animate gradients is to use pre-generated
161 images or SVG drawings containing gradients.
167 \qmlproperty list<GradientStop> Gradient::stops
168 This property holds the gradient stops describing the gradient.
170 By default, this property contains an empty list.
172 To set the gradient stops, define them as children of the Gradient element.
175 const QGradient *QDeclarativeGradient::gradient() const
177 if (!m_gradient && !m_stops.isEmpty()) {
178 m_gradient = new QLinearGradient(0,0,0,1.0);
179 for (int i = 0; i < m_stops.count(); ++i) {
180 const QDeclarativeGradientStop *stop = m_stops.at(i);
181 m_gradient->setCoordinateMode(QGradient::ObjectBoundingMode);
182 m_gradient->setColorAt(stop->position(), stop->color());
189 void QDeclarativeGradient::doUpdate()
198 \qmlclass Rectangle QDeclarativeRectangle
199 \ingroup qml-basic-visual-elements
201 \brief The Rectangle item provides a filled rectangle with an optional border.
204 Rectangle items are used to fill areas with solid color or gradients, and are
205 often used to hold other items.
209 Each Rectangle item is painted using either a solid fill color, specified using
210 the \l color property, or a gradient, defined using a Gradient element and set
211 using the \l gradient property. If both a color and a gradient are specified,
212 the gradient is used.
214 You can add an optional border to a rectangle with its own color and thickness
215 by settting the \l border.color and \l border.width properties.
217 You can also create rounded rectangles using the \l radius property. Since this
218 introduces curved edges to the corners of a rectangle, it may be appropriate to
219 set the \l smooth property to improve its appearance.
221 \section1 Example Usage
223 \div {class="float-right"}
224 \inlineimage declarative-rect.png
227 The following example shows the effects of some of the common properties on a
228 Rectangle item, which in this case is used to create a square:
230 \snippet doc/src/snippets/declarative/rectangle/rectangle.qml document
233 \section1 Performance
235 Using the \l smooth property improves the appearance of a rounded rectangle at
236 the cost of rendering performance. You should consider unsetting this property
237 for rectangles in motion, and only set it when they are stationary.
242 int QDeclarativeRectanglePrivate::doUpdateSlotIdx = -1;
244 QDeclarativeRectangle::QDeclarativeRectangle(QDeclarativeItem *parent)
245 : QDeclarativeItem(*(new QDeclarativeRectanglePrivate), parent)
249 void QDeclarativeRectangle::doUpdate()
251 Q_D(QDeclarativeRectangle);
252 d->rectImage = QPixmap();
253 const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0;
254 d->setPaintMargin((pw+1)/2);
259 \qmlproperty int Rectangle::border.width
260 \qmlproperty color Rectangle::border.color
262 The width and color used to draw the border of the rectangle.
264 A width of 1 creates a thin line. For no line, use a width of 0 or a transparent color.
266 \note The width of the rectangle's border does not affect the geometry of the
267 rectangle itself or its position relative to other items if anchors are used.
269 If \c border.width is an odd number, the rectangle is painted at a half-pixel offset to retain
270 border smoothness. Also, the border is rendered evenly on either side of the
271 rectangle's boundaries, and the spare pixel is rendered to the right and below the
272 rectangle (as documented for QRect rendering). This can cause unintended effects if
273 \c border.width is 1 and the rectangle is \l{Item::clip}{clipped} by a parent item:
275 \div {class="float-right"}
276 \inlineimage rect-border-width.png
279 \snippet doc/src/snippets/declarative/rectangle/rect-border-width.qml 0
282 Here, the innermost rectangle's border is clipped on the bottom and right edges by its
283 parent. To avoid this, the border width can be set to two instead of one.
285 QDeclarativePen *QDeclarativeRectangle::border()
287 Q_D(QDeclarativeRectangle);
292 \qmlproperty Gradient Rectangle::gradient
294 The gradient to use to fill the rectangle.
296 This property allows for the construction of simple vertical gradients.
297 Other gradients may by formed by adding rotation to the rectangle.
299 \div {class="float-left"}
300 \inlineimage declarative-rect_gradient.png
303 \snippet doc/src/snippets/declarative/rectangle/rectangle-gradient.qml rectangles
306 If both a gradient and a color are specified, the gradient will be used.
310 QDeclarativeGradient *QDeclarativeRectangle::gradient() const
312 Q_D(const QDeclarativeRectangle);
316 void QDeclarativeRectangle::setGradient(QDeclarativeGradient *gradient)
318 Q_D(QDeclarativeRectangle);
319 if (d->gradient == gradient)
321 static int updatedSignalIdx = -1;
322 if (updatedSignalIdx < 0)
323 updatedSignalIdx = QDeclarativeGradient::staticMetaObject.indexOfSignal("updated()");
324 if (d->doUpdateSlotIdx < 0)
325 d->doUpdateSlotIdx = QDeclarativeRectangle::staticMetaObject.indexOfSlot("doUpdate()");
327 QMetaObject::disconnect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
328 d->gradient = gradient;
330 QMetaObject::connect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
336 \qmlproperty real Rectangle::radius
337 This property holds the corner radius used to draw a rounded rectangle.
339 If radius is non-zero, the rectangle will be painted as a rounded rectangle, otherwise it will be
340 painted as a normal rectangle. The same radius is used by all 4 corners; there is currently
341 no way to specify different radii for different corners.
343 qreal QDeclarativeRectangle::radius() const
345 Q_D(const QDeclarativeRectangle);
349 void QDeclarativeRectangle::setRadius(qreal radius)
351 Q_D(QDeclarativeRectangle);
352 if (d->radius == radius)
356 d->rectImage = QPixmap();
358 emit radiusChanged();
362 \qmlproperty color Rectangle::color
363 This property holds the color used to fill the rectangle.
365 The default color is white.
367 \div {class="float-right"}
368 \inlineimage rect-color.png
371 The following example shows rectangles with colors specified
372 using hexadecimal and named color notation:
374 \snippet doc/src/snippets/declarative/rectangle/rectangle-colors.qml rectangles
377 If both a gradient and a color are specified, the gradient will be used.
381 QColor QDeclarativeRectangle::color() const
383 Q_D(const QDeclarativeRectangle);
387 void QDeclarativeRectangle::setColor(const QColor &c)
389 Q_D(QDeclarativeRectangle);
394 d->rectImage = QPixmap();
399 void QDeclarativeRectangle::generateRoundedRect()
401 Q_D(QDeclarativeRectangle);
402 if (d->rectImage.isNull()) {
403 const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0;
404 const int radius = qCeil(d->radius); //ensure odd numbered width/height so we get 1-pixel center
406 QString key = QLatin1String("q_") % QString::number(pw) % d->color.name() % QString::number(d->color.alpha(), 16) % QLatin1Char('_') % QString::number(radius);
407 if (d->pen && d->pen->isValid())
408 key += d->pen->color().name() % QString::number(d->pen->color().alpha(), 16);
410 if (!QPixmapCache::find(key, &d->rectImage)) {
411 d->rectImage = QPixmap(radius*2 + 3 + pw*2, radius*2 + 3 + pw*2);
412 d->rectImage.fill(Qt::transparent);
413 QPainter p(&(d->rectImage));
414 p.setRenderHint(QPainter::Antialiasing);
415 if (d->pen && d->pen->isValid()) {
416 QPen pn(QColor(d->pen->color()), d->pen->width());
421 p.setBrush(d->color);
423 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);
425 p.drawRoundedRect(QRectF(qreal(pw)/2, qreal(pw)/2, d->rectImage.width()-pw, d->rectImage.height()-pw), d->radius, d->radius);
427 // end painting before inserting pixmap
428 // to pixmap cache to avoid a deep copy
430 QPixmapCache::insert(key, d->rectImage);
435 void QDeclarativeRectangle::generateBorderedRect()
437 Q_D(QDeclarativeRectangle);
438 if (d->rectImage.isNull()) {
439 const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0;
441 QString key = QLatin1String("q_") % QString::number(pw) % d->color.name() % QString::number(d->color.alpha(), 16);
442 if (d->pen && d->pen->isValid())
443 key += d->pen->color().name() % QString::number(d->pen->color().alpha(), 16);
445 if (!QPixmapCache::find(key, &d->rectImage)) {
446 // Adding 5 here makes qDrawBorderPixmap() paint correctly with smooth: true
447 // See QTBUG-7999 and QTBUG-10765 for more details.
448 d->rectImage = QPixmap(pw*2 + 5, pw*2 + 5);
449 d->rectImage.fill(Qt::transparent);
450 QPainter p(&(d->rectImage));
451 p.setRenderHint(QPainter::Antialiasing);
452 if (d->pen && d->pen->isValid()) {
453 QPen pn(QColor(d->pen->color()), d->pen->width());
454 pn.setJoinStyle(Qt::MiterJoin);
459 p.setBrush(d->color);
461 p.drawRect(QRectF(qreal(pw)/2+1, qreal(pw)/2+1, d->rectImage.width()-(pw+1), d->rectImage.height()-(pw+1)));
463 p.drawRect(QRectF(qreal(pw)/2, qreal(pw)/2, d->rectImage.width()-pw, d->rectImage.height()-pw));
465 // end painting before inserting pixmap
466 // to pixmap cache to avoid a deep copy
468 QPixmapCache::insert(key, d->rectImage);
473 void QDeclarativeRectangle::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
475 Q_D(QDeclarativeRectangle);
476 if (width() <= 0 || height() <= 0)
478 if (d->radius > 0 || (d->pen && d->pen->isValid())
479 || (d->gradient && d->gradient->gradient()) ) {
483 bool oldAA = p->testRenderHint(QPainter::Antialiasing);
485 p->setRenderHints(QPainter::Antialiasing, true);
486 p->fillRect(QRectF(0, 0, width(), height()), d->color);
488 p->setRenderHint(QPainter::Antialiasing, oldAA);
492 void QDeclarativeRectangle::drawRect(QPainter &p)
494 Q_D(QDeclarativeRectangle);
495 if ((d->gradient && d->gradient->gradient())
496 || d->radius > width()/2 || d->radius > height()/2
497 || width() < 3 || height() < 3) {
498 // XXX This path is still slower than the image path
499 // Image path won't work for gradients or invalid radius though
500 bool oldAA = p.testRenderHint(QPainter::Antialiasing);
502 p.setRenderHint(QPainter::Antialiasing);
503 if (d->pen && d->pen->isValid()) {
504 QPen pn(QColor(d->pen->color()), d->pen->width());
505 pn.setJoinStyle(Qt::MiterJoin);
510 if (d->gradient && d->gradient->gradient())
511 p.setBrush(*d->gradient->gradient());
513 p.setBrush(d->color);
514 const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0;
517 rect = QRectF(0.5, 0.5, width()-1, height()-1);
519 rect = QRectF(0, 0, width(), height());
520 qreal radius = d->radius;
521 if (radius > width()/2 || radius > height()/2)
522 radius = qMin(width()/2, height()/2);
524 p.drawRoundedRect(rect, radius, radius);
528 p.setRenderHint(QPainter::Antialiasing, oldAA);
530 bool oldAA = p.testRenderHint(QPainter::Antialiasing);
531 bool oldSmooth = p.testRenderHint(QPainter::SmoothPixmapTransform);
533 p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth);
535 const int pw = d->pen && d->pen->isValid() ? (d->pen->width()+1)/2*2 : 0;
538 generateRoundedRect();
540 generateBorderedRect();
542 int xOffset = (d->rectImage.width()-1)/2;
543 int yOffset = (d->rectImage.height()-1)/2;
544 Q_ASSERT(d->rectImage.width() == 2*xOffset + 1);
545 Q_ASSERT(d->rectImage.height() == 2*yOffset + 1);
547 // check whether we've eliminated the center completely
548 if (2*xOffset > width()+pw)
549 xOffset = (width()+pw)/2;
550 if (2*yOffset > height()+pw)
551 yOffset = (height()+pw)/2;
553 QMargins margins(xOffset, yOffset, xOffset, yOffset);
554 QTileRules rules(Qt::StretchTile, Qt::StretchTile);
555 //NOTE: even though our item may have qreal-based width and height, qDrawBorderPixmap only supports QRects
556 qDrawBorderPixmap(&p, QRect(-pw/2, -pw/2, width()+pw, height()+pw), margins, d->rectImage, d->rectImage.rect(), margins, rules);
559 p.setRenderHint(QPainter::Antialiasing, oldAA);
560 p.setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth);
566 \qmlproperty bool Rectangle::smooth
568 Set this property if you want the item to be smoothly scaled or
569 transformed. Smooth filtering gives better visual quality, but is slower. If
570 the item is displayed at its natural size, this property has no visual or
573 \note Generally scaling artifacts are only visible if the item is stationary on
574 the screen. A common pattern when animating an item is to disable smooth
575 filtering at the beginning of the animation and reenable it at the conclusion.
577 \image rect-smooth.png
578 On this image, smooth is turned off on the top half and on on the bottom half.
581 QRectF QDeclarativeRectangle::boundingRect() const
583 Q_D(const QDeclarativeRectangle);
584 return QRectF(-d->paintmargin, -d->paintmargin, d->width()+d->paintmargin*2, d->height()+d->paintmargin*2);