source: trunk/src/gui/widgets/qcommandlinkbutton.cpp@ 846

Last change on this file since 846 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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