src/gui/widgets/qcommandlinkbutton.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 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 QtGui 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 "qcommandlinkbutton.h"
       
    43 #include "qstylepainter.h"
       
    44 #include "qstyleoption.h"
       
    45 #include "qtextdocument.h"
       
    46 #include "qtextlayout.h"
       
    47 #include "qcolor.h"
       
    48 #include "qfont.h"
       
    49 
       
    50 #include "private/qpushbutton_p.h"
       
    51 
       
    52 QT_BEGIN_NAMESPACE
       
    53 
       
    54 /*!
       
    55     \class QCommandLinkButton
       
    56     \since 4.4
       
    57     \brief The QCommandLinkButton widget provides a Vista style command link button.
       
    58 
       
    59     \ingroup basicwidgets
       
    60 
       
    61 
       
    62     The command link is a new control that was introduced by Windows Vista. It's
       
    63     intended use is similar to that of a radio button in that it is used to choose
       
    64     between a set of mutually exclusive options. Command link buttons should not
       
    65     be used by themselves but rather as an alternative to radio buttons in
       
    66     Wizards and dialogs and makes pressing the "next" button redundant.
       
    67     The appearance is generally similar to that of a flat pushbutton, but
       
    68     it allows for a descriptive text in addition to the normal button text.
       
    69     By default it will also carry an arrow icon, indicating that pressing the
       
    70     control will open another window or page.
       
    71 
       
    72     \sa QPushButton QRadioButton
       
    73 */
       
    74 
       
    75 /*!
       
    76     \property QCommandLinkButton::description
       
    77     \brief A descriptive label to complement the button text
       
    78 
       
    79     Setting this property will set a descriptive text on the
       
    80     button, complementing the text label. This will usually
       
    81     be displayed in a smaller font than the primary text.
       
    82 */
       
    83 
       
    84 /*!
       
    85     \property QCommandLinkButton::flat
       
    86     \brief This property determines whether the button is displayed as a flat
       
    87     panel or with a border.
       
    88 
       
    89     By default, this property is set to false.
       
    90 
       
    91     \sa QPushButton::flat
       
    92 */
       
    93 
       
    94 class QCommandLinkButtonPrivate : public QPushButtonPrivate
       
    95 {
       
    96     Q_DECLARE_PUBLIC(QCommandLinkButton)
       
    97 
       
    98 public:
       
    99     QCommandLinkButtonPrivate()
       
   100         : QPushButtonPrivate(){}
       
   101 
       
   102     void init();
       
   103     qreal titleSize() const;
       
   104     bool usingVistaStyle() const;
       
   105 
       
   106     QFont titleFont() const;
       
   107     QFont descriptionFont() const;
       
   108 
       
   109     QRect titleRect() const;
       
   110     QRect descriptionRect() const;
       
   111 
       
   112     int textOffset() const;
       
   113     int descriptionOffset() const;
       
   114     int descriptionHeight(int width) const;
       
   115     QColor mergedColors(const QColor &a, const QColor &b, int value) const;
       
   116 
       
   117     int topMargin() const { return 10; }
       
   118     int leftMargin() const { return 7; }
       
   119     int rightMargin() const { return 4; }
       
   120     int bottomMargin() const { return 4; }
       
   121 
       
   122     QString description;
       
   123     QColor currentColor;
       
   124 };
       
   125 
       
   126 // Mix colors a and b with a ratio in the range [0-255]
       
   127 QColor QCommandLinkButtonPrivate::mergedColors(const QColor &a, const QColor &b, int value = 50) const
       
   128 {
       
   129     Q_ASSERT(value >= 0);
       
   130     Q_ASSERT(value <= 255);
       
   131     QColor tmp = a;
       
   132     tmp.setRed((tmp.red() * value) / 255 + (b.red() * (255 - value)) / 255);
       
   133     tmp.setGreen((tmp.green() * value) / 255 + (b.green() * (255 - value)) / 255);
       
   134     tmp.setBlue((tmp.blue() * value) / 255 + (b.blue() * (255 - value)) / 255);
       
   135     return tmp;
       
   136 }
       
   137 
       
   138 QFont QCommandLinkButtonPrivate::titleFont() const
       
   139 {
       
   140     Q_Q(const QCommandLinkButton);
       
   141     QFont font = q->font();
       
   142     if (usingVistaStyle()) {
       
   143         font.setPointSizeF(12.0);
       
   144     } else {
       
   145         font.setBold(true);
       
   146         font.setPointSizeF(9.0);
       
   147     }
       
   148 
       
   149     // Note the font will be resolved against
       
   150     // QPainters font, so we need to restore the mask
       
   151     int resolve_mask = font.resolve_mask;
       
   152     QFont modifiedFont = q->font().resolve(font);
       
   153     modifiedFont.detach();
       
   154     modifiedFont.resolve_mask = resolve_mask;
       
   155     return modifiedFont;
       
   156 }
       
   157 
       
   158 QFont QCommandLinkButtonPrivate::descriptionFont() const
       
   159 {
       
   160     Q_Q(const QCommandLinkButton);
       
   161     QFont font = q->font();
       
   162     font.setPointSizeF(9.0);
       
   163 
       
   164     // Note the font will be resolved against
       
   165     // QPainters font, so we need to restore the mask
       
   166     int resolve_mask = font.resolve_mask;
       
   167     QFont modifiedFont = q->font().resolve(font);
       
   168     modifiedFont.detach();
       
   169     modifiedFont.resolve_mask = resolve_mask;
       
   170     return modifiedFont;
       
   171 }
       
   172 
       
   173 QRect QCommandLinkButtonPrivate::titleRect() const
       
   174 {
       
   175     Q_Q(const QCommandLinkButton);
       
   176     return q->rect().adjusted(textOffset(), topMargin(),
       
   177                               -rightMargin(), 0);
       
   178 }
       
   179 
       
   180 QRect QCommandLinkButtonPrivate::descriptionRect() const
       
   181 {
       
   182     Q_Q(const QCommandLinkButton);
       
   183     return q->rect().adjusted(textOffset(), descriptionOffset(),
       
   184                               -rightMargin(), -bottomMargin());
       
   185 }
       
   186 
       
   187 int QCommandLinkButtonPrivate::textOffset() const
       
   188 {
       
   189     Q_Q(const QCommandLinkButton);
       
   190     return q->icon().actualSize(q->iconSize()).width() + leftMargin() + 6;
       
   191 }
       
   192 
       
   193 int QCommandLinkButtonPrivate::descriptionOffset() const
       
   194 {
       
   195     QFontMetrics fm(titleFont());
       
   196     return topMargin() + fm.height();
       
   197 }
       
   198 
       
   199 bool QCommandLinkButtonPrivate::usingVistaStyle() const
       
   200 {
       
   201     Q_Q(const QCommandLinkButton);
       
   202     //### This is a hack to detect if we are indeed running Vista style themed and not in classic
       
   203     // When we add api to query for this, we should change this implementation to use it.
       
   204     return q->style()->inherits("QWindowsVistaStyle")
       
   205         && !q->style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal);
       
   206 }
       
   207 
       
   208 void QCommandLinkButtonPrivate::init()
       
   209 {
       
   210     Q_Q(QCommandLinkButton);
       
   211     QPushButtonPrivate::init();
       
   212     q->setAttribute(Qt::WA_Hover);
       
   213 
       
   214     QSizePolicy policy(QSizePolicy::Preferred, QSizePolicy::Preferred, QSizePolicy::PushButton);
       
   215     policy.setHeightForWidth(true);
       
   216     q->setSizePolicy(policy);
       
   217 
       
   218     q->setIconSize(QSize(20, 20));
       
   219     QStyleOptionButton opt;
       
   220     q->initStyleOption(&opt);
       
   221     q->setIcon(q->style()->standardIcon(QStyle::SP_CommandLink, &opt));
       
   222 }
       
   223 
       
   224 // Calculates the height of the description text based on widget width
       
   225 int QCommandLinkButtonPrivate::descriptionHeight(int widgetWidth) const
       
   226 {
       
   227     // Calc width of actual paragraph
       
   228     int lineWidth = widgetWidth - textOffset() - rightMargin();
       
   229 
       
   230     qreal descriptionheight = 0;
       
   231     if (!description.isEmpty()) {
       
   232         QTextLayout layout(description);
       
   233         layout.setFont(descriptionFont());
       
   234         layout.beginLayout();
       
   235         while (true) {
       
   236             QTextLine line = layout.createLine();
       
   237             if (!line.isValid())
       
   238                 break;
       
   239             line.setLineWidth(lineWidth);
       
   240             line.setPosition(QPointF(0, descriptionheight));
       
   241             descriptionheight += line.height();
       
   242         }
       
   243         layout.endLayout();
       
   244     }
       
   245     return qRound(descriptionheight);
       
   246 }
       
   247 
       
   248 /*!
       
   249     \reimp
       
   250  */
       
   251 QSize QCommandLinkButton::minimumSizeHint() const
       
   252 {
       
   253     Q_D(const QCommandLinkButton);
       
   254     QSize size = sizeHint();
       
   255     int minimumHeight = qMax(d->descriptionOffset() + d->bottomMargin(),
       
   256                              iconSize().height() + d->topMargin());
       
   257     size.setHeight(minimumHeight);
       
   258     return size;
       
   259 }
       
   260 
       
   261 /*!
       
   262     Constructs a command link with no text and a \a parent.
       
   263 */
       
   264 
       
   265 QCommandLinkButton::QCommandLinkButton(QWidget *parent)
       
   266 : QPushButton(*new QCommandLinkButtonPrivate, parent)
       
   267 {
       
   268     Q_D(QCommandLinkButton);
       
   269     d->init();
       
   270 }
       
   271 
       
   272 /*!
       
   273     Constructs a command link with the parent \a parent and the text \a
       
   274     text.
       
   275 */
       
   276 
       
   277 QCommandLinkButton::QCommandLinkButton(const QString &text, QWidget *parent)
       
   278     : QPushButton(*new QCommandLinkButtonPrivate, parent)
       
   279 {
       
   280     Q_D(QCommandLinkButton);
       
   281     setText(text);
       
   282     d->init();
       
   283 }
       
   284 
       
   285 /*!
       
   286     Constructs a command link with a \a text, a \a description, and a \a parent.
       
   287 */
       
   288 QCommandLinkButton::QCommandLinkButton(const QString &text, const QString &description, QWidget *parent)
       
   289     : QPushButton(*new QCommandLinkButtonPrivate, parent)
       
   290 {
       
   291     Q_D(QCommandLinkButton);
       
   292     setText(text);
       
   293     setDescription(description);
       
   294     d->init();
       
   295 }
       
   296 
       
   297 /*! \reimp */
       
   298 bool QCommandLinkButton::event(QEvent *e)
       
   299 {
       
   300     return QPushButton::event(e);
       
   301 }
       
   302 
       
   303 /*! \reimp */
       
   304 QSize QCommandLinkButton::sizeHint() const
       
   305 {
       
   306 //  Standard size hints from UI specs
       
   307 //  Without note: 135, 41
       
   308 //  With note: 135, 60
       
   309     Q_D(const QCommandLinkButton);
       
   310 
       
   311     QSize size = QPushButton::sizeHint();
       
   312     QFontMetrics fm(d->titleFont());
       
   313     int textWidth = qMax(fm.width(text()), 135);
       
   314     int buttonWidth = textWidth + d->textOffset() + d->rightMargin();
       
   315     int heightWithoutDescription = d->descriptionOffset() + d->bottomMargin();
       
   316 
       
   317     size.setWidth(qMax(size.width(), buttonWidth));
       
   318     size.setHeight(qMax(d->description.isEmpty() ? 41 : 60,
       
   319                         heightWithoutDescription + d->descriptionHeight(buttonWidth)));
       
   320     return size;
       
   321 }
       
   322 
       
   323 /*! \reimp */
       
   324 int QCommandLinkButton::heightForWidth(int width) const
       
   325 {
       
   326     Q_D(const QCommandLinkButton);
       
   327     int heightWithoutDescription = d->descriptionOffset() + d->bottomMargin();
       
   328     // find the width available for the description area
       
   329     return heightWithoutDescription + d->descriptionHeight(width);
       
   330 }
       
   331 
       
   332 /*! \reimp */
       
   333 void QCommandLinkButton::paintEvent(QPaintEvent *)
       
   334 {
       
   335     Q_D(QCommandLinkButton);
       
   336     QStylePainter p(this);
       
   337     p.save();
       
   338 
       
   339     QStyleOptionButton option;
       
   340     initStyleOption(&option);
       
   341 
       
   342     //Enable command link appearence on Vista
       
   343     option.features |= QStyleOptionButton::CommandLinkButton;
       
   344     option.text = QString();
       
   345     option.icon = QIcon(); //we draw this ourselves
       
   346     QSize pixmapSize = icon().actualSize(iconSize());
       
   347 
       
   348     int vOffset = isDown() ? style()->pixelMetric(QStyle::PM_ButtonShiftVertical) : 0;
       
   349     int hOffset = isDown() ? style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal) : 0;
       
   350 
       
   351     //Draw icon
       
   352     p.drawControl(QStyle::CE_PushButton, option);
       
   353     if (!icon().isNull())
       
   354         p.drawPixmap(d->leftMargin() + hOffset, d->topMargin() + vOffset,
       
   355         icon().pixmap(pixmapSize, isEnabled() ? QIcon::Normal : QIcon::Disabled,
       
   356                                   isChecked() ? QIcon::On : QIcon::Off));
       
   357 
       
   358     //Draw title
       
   359     QColor textColor = palette().buttonText().color();
       
   360     if (isEnabled() && d->usingVistaStyle()) {
       
   361         textColor = QColor(21, 28, 85);
       
   362         if (underMouse() && !isDown())
       
   363             textColor = QColor(7, 64, 229);
       
   364         //A simple text color transition
       
   365         d->currentColor = d->mergedColors(textColor, d->currentColor, 60);
       
   366         option.palette.setColor(QPalette::ButtonText, d->currentColor);
       
   367     }
       
   368 
       
   369     int textflags = Qt::TextShowMnemonic;
       
   370     if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &option, this))
       
   371         textflags |= Qt::TextHideMnemonic;
       
   372 
       
   373     p.setFont(d->titleFont());
       
   374     p.drawItemText(d->titleRect().translated(hOffset, vOffset),
       
   375                     textflags, option.palette, isEnabled(), text(), QPalette::ButtonText);
       
   376 
       
   377     //Draw description
       
   378     textflags |= Qt::TextWordWrap | Qt::ElideRight;
       
   379     p.setFont(d->descriptionFont());
       
   380     p.drawItemText(d->descriptionRect().translated(hOffset, vOffset), textflags,
       
   381                     option.palette, isEnabled(), description(), QPalette::ButtonText);
       
   382     p.restore();
       
   383 }
       
   384 
       
   385 void QCommandLinkButton::setDescription(const QString &description)
       
   386 {
       
   387     Q_D(QCommandLinkButton);
       
   388     d->description = description;
       
   389     updateGeometry();
       
   390     update();
       
   391 }
       
   392 
       
   393 QString QCommandLinkButton::description() const
       
   394 {
       
   395     Q_D(const QCommandLinkButton);
       
   396     return d->description;
       
   397 }
       
   398 
       
   399 QT_END_NAMESPACE
       
   400