src/declarative/graphicsitems/qdeclarativerectangle.cpp
changeset 30 5dc02b23752f
child 33 3e2da88830cd
equal deleted inserted replaced
29:b72c6db6890b 30:5dc02b23752f
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 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 ** 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
       
    14 ** this package.
       
    15 **
       
    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.
       
    23 **
       
    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.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "private/qdeclarativerectangle_p.h"
       
    43 #include "private/qdeclarativerectangle_p_p.h"
       
    44 
       
    45 #include <QPainter>
       
    46 #include <QStringBuilder>
       
    47 #include <QtCore/qmath.h>
       
    48 
       
    49 QT_BEGIN_NAMESPACE
       
    50 
       
    51 /*!
       
    52     \internal
       
    53     \class QDeclarativePen
       
    54     \brief The QDeclarativePen class provides a pen used for drawing rectangle borders on a QDeclarativeView.
       
    55 
       
    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).
       
    58 
       
    59     A width of 1 indicates is a single-pixel line on the border of the item being painted.
       
    60 
       
    61     Example:
       
    62     \qml
       
    63     Rectangle { border.width: 2; border.color: "red" ... }
       
    64     \endqml
       
    65 */
       
    66 
       
    67 void QDeclarativePen::setColor(const QColor &c)
       
    68 {
       
    69     _color = c;
       
    70     _valid = _color.alpha() ? true : false;
       
    71     emit penChanged();
       
    72 }
       
    73 
       
    74 void QDeclarativePen::setWidth(int w)
       
    75 {
       
    76     if (_width == w && _valid)
       
    77         return;
       
    78 
       
    79     _width = w;
       
    80     _valid = (_width < 1) ? false : true;
       
    81     emit penChanged();
       
    82 }
       
    83 
       
    84 
       
    85 /*!
       
    86     \qmlclass GradientStop QDeclarativeGradientStop
       
    87   \since 4.7
       
    88     \brief The GradientStop item defines the color at a position in a Gradient
       
    89 
       
    90     \sa Gradient
       
    91 */
       
    92 
       
    93 /*!
       
    94     \qmlproperty real GradientStop::position
       
    95     \qmlproperty color GradientStop::color
       
    96 
       
    97     Sets a \e color at a \e position in a gradient.
       
    98 */
       
    99 
       
   100 void QDeclarativeGradientStop::updateGradient()
       
   101 {
       
   102     if (QDeclarativeGradient *grad = qobject_cast<QDeclarativeGradient*>(parent()))
       
   103         grad->doUpdate();
       
   104 }
       
   105 
       
   106 /*!
       
   107     \qmlclass Gradient QDeclarativeGradient
       
   108   \since 4.7
       
   109     \brief The Gradient item defines a gradient fill.
       
   110 
       
   111     A gradient is defined by two or more colors, which will be blended seemlessly.  The
       
   112     colors are specified at their position in the range 0.0 - 1.0 via
       
   113     the GradientStop item.  For example, the following code paints a
       
   114     rectangle with a gradient starting with red, blending to yellow at 1/3 of the
       
   115     size of the rectangle, and ending with Green:
       
   116 
       
   117     \table
       
   118     \row
       
   119     \o \image gradient.png
       
   120     \o \quotefile doc/src/snippets/declarative/gradient.qml
       
   121     \endtable
       
   122 
       
   123     \sa GradientStop
       
   124 */
       
   125 
       
   126 /*!
       
   127     \qmlproperty list<GradientStop> Gradient::stops
       
   128     This property holds the gradient stops describing the gradient.
       
   129 */
       
   130 
       
   131 const QGradient *QDeclarativeGradient::gradient() const
       
   132 {
       
   133     if (!m_gradient && !m_stops.isEmpty()) {
       
   134         m_gradient = new QLinearGradient(0,0,0,1.0);
       
   135         for (int i = 0; i < m_stops.count(); ++i) {
       
   136             const QDeclarativeGradientStop *stop = m_stops.at(i);
       
   137             m_gradient->setCoordinateMode(QGradient::ObjectBoundingMode);
       
   138             m_gradient->setColorAt(stop->position(), stop->color());
       
   139         }
       
   140     }
       
   141 
       
   142     return m_gradient;
       
   143 }
       
   144 
       
   145 void QDeclarativeGradient::doUpdate()
       
   146 {
       
   147     delete m_gradient;
       
   148     m_gradient = 0;
       
   149     emit updated();
       
   150 }
       
   151 
       
   152 
       
   153 /*!
       
   154     \qmlclass Rectangle QDeclarativeRectangle
       
   155   \since 4.7
       
   156     \brief The Rectangle item allows you to add rectangles to a scene.
       
   157     \inherits Item
       
   158 
       
   159     A Rectangle is painted using a solid fill (color) and an optional border.
       
   160     You can also create rounded rectangles using the \l radius property.
       
   161 
       
   162     \qml
       
   163     Rectangle {
       
   164         width: 100
       
   165         height: 100
       
   166         color: "red"
       
   167         border.color: "black"
       
   168         border.width: 5
       
   169         radius: 10
       
   170     }
       
   171     \endqml
       
   172 
       
   173     \image declarative-rect.png
       
   174 */
       
   175 
       
   176 int QDeclarativeRectanglePrivate::doUpdateSlotIdx = -1;
       
   177 
       
   178 /*!
       
   179     \internal
       
   180     \class QDeclarativeRectangle
       
   181     \brief The QDeclarativeRectangle class provides a rectangle item that you can add to a QDeclarativeView.
       
   182 */
       
   183 QDeclarativeRectangle::QDeclarativeRectangle(QDeclarativeItem *parent)
       
   184   : QDeclarativeItem(*(new QDeclarativeRectanglePrivate), parent)
       
   185 {
       
   186 }
       
   187 
       
   188 void QDeclarativeRectangle::doUpdate()
       
   189 {
       
   190     Q_D(QDeclarativeRectangle);
       
   191     d->rectImage = QPixmap();
       
   192     const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0;
       
   193     d->setPaintMargin((pw+1)/2);
       
   194     update();
       
   195 }
       
   196 
       
   197 /*!
       
   198     \qmlproperty int Rectangle::border.width
       
   199     \qmlproperty color Rectangle::border.color
       
   200 
       
   201     The width and color used to draw the border of the rectangle.
       
   202 
       
   203     A width of 1 creates a thin line. For no line, use a width of 0 or a transparent color.
       
   204 
       
   205     To keep the border smooth (rather than blurry), odd widths cause the rectangle to be painted at
       
   206     a half-pixel offset;
       
   207 */
       
   208 QDeclarativePen *QDeclarativeRectangle::border()
       
   209 {
       
   210     Q_D(QDeclarativeRectangle);
       
   211     return d->getPen();
       
   212 }
       
   213 
       
   214 /*!
       
   215     \qmlproperty Gradient Rectangle::gradient
       
   216 
       
   217     The gradient to use to fill the rectangle.
       
   218 
       
   219     This property allows for the construction of simple vertical gradients.
       
   220     Other gradients may by formed by adding rotation to the rectangle.
       
   221 
       
   222     \table
       
   223     \row
       
   224     \o \image declarative-rect_gradient.png
       
   225     \o
       
   226     \qml
       
   227     Rectangle {
       
   228         y: 0; width: 80; height: 80
       
   229         color: "lightsteelblue"
       
   230     }
       
   231 
       
   232     Rectangle {
       
   233         y: 100; width: 80; height: 80
       
   234         gradient: Gradient {
       
   235             GradientStop { position: 0.0; color: "lightsteelblue" }
       
   236             GradientStop { position: 1.0; color: "blue" }
       
   237         }
       
   238     }
       
   239 
       
   240     Rectangle {
       
   241         y: 200; width: 80; height: 80
       
   242         rotation: 90
       
   243         gradient: Gradient {
       
   244             GradientStop { position: 0.0; color: "lightsteelblue" }
       
   245             GradientStop { position: 1.0; color: "blue" }
       
   246         }
       
   247     }
       
   248     \endqml
       
   249     \endtable
       
   250 
       
   251     If both a gradient and a color are specified, the gradient will be used.
       
   252 
       
   253     \sa Gradient, color
       
   254 */
       
   255 QDeclarativeGradient *QDeclarativeRectangle::gradient() const
       
   256 {
       
   257     Q_D(const QDeclarativeRectangle);
       
   258     return d->gradient;
       
   259 }
       
   260 
       
   261 void QDeclarativeRectangle::setGradient(QDeclarativeGradient *gradient)
       
   262 {
       
   263     Q_D(QDeclarativeRectangle);
       
   264     if (d->gradient == gradient)
       
   265         return;
       
   266     static int updatedSignalIdx = -1;
       
   267     if (updatedSignalIdx < 0)
       
   268         updatedSignalIdx = QDeclarativeGradient::staticMetaObject.indexOfSignal("updated()");
       
   269     if (d->doUpdateSlotIdx < 0)
       
   270         d->doUpdateSlotIdx = QDeclarativeRectangle::staticMetaObject.indexOfSlot("doUpdate()");
       
   271     if (d->gradient)
       
   272         QMetaObject::disconnect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
       
   273     d->gradient = gradient;
       
   274     if (d->gradient)
       
   275         QMetaObject::connect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
       
   276     update();
       
   277 }
       
   278 
       
   279 
       
   280 /*!
       
   281     \qmlproperty real Rectangle::radius
       
   282     This property holds the corner radius used to draw a rounded rectangle.
       
   283 
       
   284     If radius is non-zero, the rectangle will be painted as a rounded rectangle, otherwise it will be
       
   285     painted as a normal rectangle. The same radius is used by all 4 corners; there is currently
       
   286     no way to specify different radii for different corners.
       
   287 */
       
   288 qreal QDeclarativeRectangle::radius() const
       
   289 {
       
   290     Q_D(const QDeclarativeRectangle);
       
   291     return d->radius;
       
   292 }
       
   293 
       
   294 void QDeclarativeRectangle::setRadius(qreal radius)
       
   295 {
       
   296     Q_D(QDeclarativeRectangle);
       
   297     if (d->radius == radius)
       
   298         return;
       
   299 
       
   300     d->radius = radius;
       
   301     d->rectImage = QPixmap();
       
   302     update();
       
   303     emit radiusChanged();
       
   304 }
       
   305 
       
   306 /*!
       
   307     \qmlproperty color Rectangle::color
       
   308     This property holds the color used to fill the rectangle.
       
   309 
       
   310     \qml
       
   311     // green rectangle using hexidecimal notation
       
   312     Rectangle { color: "#00FF00" }
       
   313 
       
   314     // steelblue rectangle using SVG color name
       
   315     Rectangle { color: "steelblue" }
       
   316     \endqml
       
   317 
       
   318     The default color is white.
       
   319 
       
   320     If both a gradient and a color are specified, the gradient will be used.
       
   321 */
       
   322 QColor QDeclarativeRectangle::color() const
       
   323 {
       
   324     Q_D(const QDeclarativeRectangle);
       
   325     return d->color;
       
   326 }
       
   327 
       
   328 void QDeclarativeRectangle::setColor(const QColor &c)
       
   329 {
       
   330     Q_D(QDeclarativeRectangle);
       
   331     if (d->color == c)
       
   332         return;
       
   333 
       
   334     d->color = c;
       
   335     d->rectImage = QPixmap();
       
   336     update();
       
   337     emit colorChanged();
       
   338 }
       
   339 
       
   340 void QDeclarativeRectangle::generateRoundedRect()
       
   341 {
       
   342     Q_D(QDeclarativeRectangle);
       
   343     if (d->rectImage.isNull()) {
       
   344         const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0;
       
   345         const int radius = qCeil(d->radius);    //ensure odd numbered width/height so we get 1-pixel center
       
   346 
       
   347         QString key = QLatin1String("q_") % QString::number(pw) % d->color.name() % QString::number(d->color.alpha(), 16) % QLatin1Char('_') % QString::number(radius);
       
   348         if (d->pen && d->pen->isValid())
       
   349             key += d->pen->color().name() % QString::number(d->pen->color().alpha(), 16);
       
   350 
       
   351         if (!QPixmapCache::find(key, &d->rectImage)) {
       
   352             d->rectImage = QPixmap(radius*2 + 3 + pw*2, radius*2 + 3 + pw*2);
       
   353             d->rectImage.fill(Qt::transparent);
       
   354             QPainter p(&(d->rectImage));
       
   355             p.setRenderHint(QPainter::Antialiasing);
       
   356             if (d->pen && d->pen->isValid()) {
       
   357                 QPen pn(QColor(d->pen->color()), d->pen->width());
       
   358                 p.setPen(pn);
       
   359             } else {
       
   360                 p.setPen(Qt::NoPen);
       
   361             }
       
   362             p.setBrush(d->color);
       
   363             if (pw%2)
       
   364                 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);
       
   365             else
       
   366                 p.drawRoundedRect(QRectF(qreal(pw)/2, qreal(pw)/2, d->rectImage.width()-pw, d->rectImage.height()-pw), d->radius, d->radius);
       
   367             QPixmapCache::insert(key, d->rectImage);
       
   368         }
       
   369     }
       
   370 }
       
   371 
       
   372 void QDeclarativeRectangle::generateBorderedRect()
       
   373 {
       
   374     Q_D(QDeclarativeRectangle);
       
   375     if (d->rectImage.isNull()) {
       
   376         const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0;
       
   377 
       
   378         QString key = QLatin1String("q_") % QString::number(pw) % d->color.name() % QString::number(d->color.alpha(), 16);
       
   379         if (d->pen && d->pen->isValid())
       
   380             key += d->pen->color().name() % QString::number(d->pen->color().alpha(), 16);
       
   381 
       
   382         if (!QPixmapCache::find(key, &d->rectImage)) {
       
   383             // Adding 5 here makes qDrawBorderPixmap() paint correctly with smooth: true
       
   384             // See QTBUG-7999 and QTBUG-10765 for more details.
       
   385             d->rectImage = QPixmap(pw*2 + 5, pw*2 + 5);
       
   386             d->rectImage.fill(Qt::transparent);
       
   387             QPainter p(&(d->rectImage));
       
   388             p.setRenderHint(QPainter::Antialiasing);
       
   389             if (d->pen && d->pen->isValid()) {
       
   390                 QPen pn(QColor(d->pen->color()), d->pen->width());
       
   391                 pn.setJoinStyle(Qt::MiterJoin);
       
   392                 p.setPen(pn);
       
   393             } else {
       
   394                 p.setPen(Qt::NoPen);
       
   395             }
       
   396             p.setBrush(d->color);
       
   397             if (pw%2)
       
   398                 p.drawRect(QRectF(qreal(pw)/2+1, qreal(pw)/2+1, d->rectImage.width()-(pw+1), d->rectImage.height()-(pw+1)));
       
   399             else
       
   400                 p.drawRect(QRectF(qreal(pw)/2, qreal(pw)/2, d->rectImage.width()-pw, d->rectImage.height()-pw));
       
   401             QPixmapCache::insert(key, d->rectImage);
       
   402         }
       
   403     }
       
   404 }
       
   405 
       
   406 void QDeclarativeRectangle::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
       
   407 {
       
   408     Q_D(QDeclarativeRectangle);
       
   409     if (d->radius > 0 || (d->pen && d->pen->isValid())
       
   410         || (d->gradient && d->gradient->gradient()) ) {
       
   411         drawRect(*p);
       
   412     }
       
   413     else {
       
   414         bool oldAA = p->testRenderHint(QPainter::Antialiasing);
       
   415         if (d->smooth)
       
   416             p->setRenderHints(QPainter::Antialiasing, true);
       
   417         p->fillRect(QRectF(0, 0, width(), height()), d->color);
       
   418         if (d->smooth)
       
   419             p->setRenderHint(QPainter::Antialiasing, oldAA);
       
   420     }
       
   421 }
       
   422 
       
   423 void QDeclarativeRectangle::drawRect(QPainter &p)
       
   424 {
       
   425     Q_D(QDeclarativeRectangle);
       
   426     if ((d->gradient && d->gradient->gradient())
       
   427         || d->radius > width()/2 || d->radius > height()/2) {
       
   428         // XXX This path is still slower than the image path
       
   429         // Image path won't work for gradients or invalid radius though
       
   430         bool oldAA = p.testRenderHint(QPainter::Antialiasing);
       
   431         if (d->smooth)
       
   432             p.setRenderHint(QPainter::Antialiasing);
       
   433         if (d->pen && d->pen->isValid()) {
       
   434             QPen pn(QColor(d->pen->color()), d->pen->width());
       
   435             p.setPen(pn);
       
   436         } else {
       
   437             p.setPen(Qt::NoPen);
       
   438         }
       
   439         if (d->gradient && d->gradient->gradient())
       
   440             p.setBrush(*d->gradient->gradient());
       
   441         else
       
   442             p.setBrush(d->color);
       
   443         const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0;
       
   444         QRectF rect;
       
   445         if (pw%2)
       
   446             rect = QRectF(0.5, 0.5, width()-1, height()-1);
       
   447         else
       
   448             rect = QRectF(0, 0, width(), height());
       
   449         qreal radius = d->radius;
       
   450         if (radius > width()/2 || radius > height()/2)
       
   451             radius = qMin(width()/2, height()/2);
       
   452         if (radius > 0.)
       
   453             p.drawRoundedRect(rect, radius, radius);
       
   454         else
       
   455             p.drawRect(rect);
       
   456         if (d->smooth)
       
   457             p.setRenderHint(QPainter::Antialiasing, oldAA);
       
   458     } else {
       
   459         bool oldAA = p.testRenderHint(QPainter::Antialiasing);
       
   460         bool oldSmooth = p.testRenderHint(QPainter::SmoothPixmapTransform);
       
   461         if (d->smooth)
       
   462             p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth);
       
   463 
       
   464         const int pw = d->pen && d->pen->isValid() ? (d->pen->width()+1)/2*2 : 0;
       
   465 
       
   466         if (d->radius > 0)
       
   467             generateRoundedRect();
       
   468         else
       
   469             generateBorderedRect();
       
   470 
       
   471         int xOffset = (d->rectImage.width()-1)/2;
       
   472         int yOffset = (d->rectImage.height()-1)/2;
       
   473         Q_ASSERT(d->rectImage.width() == 2*xOffset + 1);
       
   474         Q_ASSERT(d->rectImage.height() == 2*yOffset + 1);
       
   475 
       
   476         QMargins margins(xOffset, yOffset, xOffset, yOffset);
       
   477         QTileRules rules(Qt::StretchTile, Qt::StretchTile);
       
   478         //NOTE: even though our item may have qreal-based width and height, qDrawBorderPixmap only supports QRects
       
   479         qDrawBorderPixmap(&p, QRect(-pw/2, -pw/2, width()+pw, height()+pw), margins, d->rectImage, d->rectImage.rect(), margins, rules);
       
   480 
       
   481         if (d->smooth) {
       
   482             p.setRenderHint(QPainter::Antialiasing, oldAA);
       
   483             p.setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth);
       
   484         }
       
   485     }
       
   486 }
       
   487 
       
   488 /*!
       
   489     \qmlproperty bool Rectangle::smooth
       
   490 
       
   491     Set this property if you want the item to be smoothly scaled or
       
   492     transformed.  Smooth filtering gives better visual quality, but is slower.  If
       
   493     the item is displayed at its natural size, this property has no visual or
       
   494     performance effect.
       
   495 
       
   496     \note Generally scaling artifacts are only visible if the item is stationary on
       
   497     the screen.  A common pattern when animating an item is to disable smooth
       
   498     filtering at the beginning of the animation and reenable it at the conclusion.
       
   499 
       
   500     \image rect-smooth.png
       
   501     On this image, smooth is turned off on the top half and on on the bottom half.
       
   502 */
       
   503 
       
   504 QRectF QDeclarativeRectangle::boundingRect() const
       
   505 {
       
   506     Q_D(const QDeclarativeRectangle);
       
   507     return QRectF(-d->paintmargin, -d->paintmargin, d->width()+d->paintmargin*2, d->height()+d->paintmargin*2);
       
   508 }
       
   509 
       
   510 QT_END_NAMESPACE