Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qcommandlinkbutton.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
7#include "qstyleoption.h"
9#include "qtextlayout.h"
10#include "qcolor.h"
11#include "qfont.h"
12#include <qmath.h>
13
14#include "private/qpushbutton_p.h"
15#if QT_CONFIG(style_stylesheet)
16#include "private/qstylesheetstyle_p.h"
17#endif
18
20
21/*!
22 \class QCommandLinkButton
23 \since 4.4
24 \brief The QCommandLinkButton widget provides a Vista style command link button.
25
26 \ingroup basicwidgets
27 \inmodule QtWidgets
28
29 The command link is a new control that was introduced by Windows Vista. Its
30 intended use is similar to that of a radio button in that it is used to choose
31 between a set of mutually exclusive options. Command link buttons should not
32 be used by themselves but rather as an alternative to radio buttons in
33 Wizards and dialogs and makes pressing the "next" button redundant.
34 The appearance is generally similar to that of a flat pushbutton, but
35 it allows for a descriptive text in addition to the normal button text.
36 By default it will also carry an arrow icon, indicating that pressing the
37 control will open another window or page.
38
39 \sa QPushButton, QRadioButton
40*/
41
42/*!
43 \property QCommandLinkButton::description
44 \brief A descriptive label to complement the button text
45
46 Setting this property will set a descriptive text on the
47 button, complementing the text label. This will usually
48 be displayed in a smaller font than the primary text.
49*/
50
51/*!
52 \property QCommandLinkButton::flat
53 \brief This property determines whether the button is displayed as a flat
54 panel or with a border.
55
56 By default, this property is set to false.
57
58 \sa QPushButton::flat
59*/
60
62{
63 Q_DECLARE_PUBLIC(QCommandLinkButton)
64
65public:
68
69 void init();
71 bool usingVistaStyle() const;
72
73 QFont titleFont() const;
74 QFont descriptionFont() const;
75
76 QRect titleRect() const;
77 QRect descriptionRect() const;
78
79 int textOffset() const;
80 int descriptionOffset() const;
81 int descriptionHeight(int width) const;
82 QColor mergedColors(const QColor &a, const QColor &b, int value) const;
83
84 int topMargin() const { return 10; }
85 int leftMargin() const { return 7; }
86 int rightMargin() const { return 4; }
87 int bottomMargin() const { return 10; }
88
91};
92
93// Mix colors a and b with a ratio in the range [0-255]
94QColor QCommandLinkButtonPrivate::mergedColors(const QColor &a, const QColor &b, int value = 50) const
95{
96 Q_ASSERT(value >= 0);
97 Q_ASSERT(value <= 255);
98 QColor tmp = a;
99 tmp.setRed((tmp.red() * value) / 255 + (b.red() * (255 - value)) / 255);
100 tmp.setGreen((tmp.green() * value) / 255 + (b.green() * (255 - value)) / 255);
101 tmp.setBlue((tmp.blue() * value) / 255 + (b.blue() * (255 - value)) / 255);
102 return tmp;
103}
104
106{
107 Q_Q(const QCommandLinkButton);
108 QFont font = q->font();
109 if (usingVistaStyle()) {
110 font.setPointSizeF(12.0);
111 } else {
112 font.setBold(true);
113 font.setPointSizeF(9.0);
114 }
115
116 // Note the font will be resolved against
117 // QPainters font, so we need to restore the mask
118 int resolve_mask = font.resolve_mask;
119 QFont modifiedFont = q->font().resolve(font);
120 modifiedFont.detach();
121 modifiedFont.resolve_mask = resolve_mask;
122 return modifiedFont;
123}
124
126{
127 Q_Q(const QCommandLinkButton);
128 QFont font = q->font();
129 font.setPointSizeF(9.0);
130
131 // Note the font will be resolved against
132 // QPainters font, so we need to restore the mask
133 int resolve_mask = font.resolve_mask;
134 QFont modifiedFont = q->font().resolve(font);
135 modifiedFont.detach();
136 modifiedFont.resolve_mask = resolve_mask;
137 return modifiedFont;
138}
139
141{
142 Q_Q(const QCommandLinkButton);
143 QRect r = q->rect().adjusted(textOffset(), topMargin(), -rightMargin(), 0);
144 if (description.isEmpty())
145 {
146 QFontMetrics fm(titleFont());
147 r.setTop(r.top() + qMax(0, (q->icon().actualSize(q->iconSize()).height()
148 - fm.height()) / 2));
149 }
150
151 return r;
152}
153
155{
156 Q_Q(const QCommandLinkButton);
157 return q->rect().adjusted(textOffset(), descriptionOffset(),
158 -rightMargin(), -bottomMargin());
159}
160
162{
163 Q_Q(const QCommandLinkButton);
164 return q->icon().actualSize(q->iconSize()).width() + leftMargin() + 6;
165}
166
168{
169 QFontMetrics fm(titleFont());
170 return topMargin() + fm.height();
171}
172
174{
175 Q_Q(const QCommandLinkButton);
176 //### This is a hack to detect if we are indeed running Vista style themed and not in classic
177 // When we add api to query for this, we should change this implementation to use it.
178 return q->property("_qt_usingVistaStyle").toBool()
179 && q->style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal, nullptr, q) == 0;
180}
181
183{
184 Q_Q(QCommandLinkButton);
185 QPushButtonPrivate::init();
186 q->setAttribute(Qt::WA_Hover);
187 q->setAttribute(Qt::WA_MacShowFocusRect, false);
188
189 QSizePolicy policy(QSizePolicy::Preferred, QSizePolicy::Preferred, QSizePolicy::PushButton);
190 policy.setHeightForWidth(true);
191 q->setSizePolicy(policy);
192
193 q->setIconSize(QSize(20, 20));
195 q->initStyleOption(&opt);
196 q->setIcon(q->style()->standardIcon(QStyle::SP_CommandLink, &opt, q));
197}
198
199// Calculates the height of the description text based on widget width
201{
202 // Calc width of actual paragraph
203 int lineWidth = widgetWidth - textOffset() - rightMargin();
204
205 qreal descriptionheight = 0;
206 if (!description.isEmpty()) {
207 QTextLayout layout(description);
208 layout.setFont(descriptionFont());
209 layout.beginLayout();
210 while (true) {
211 QTextLine line = layout.createLine();
212 if (!line.isValid())
213 break;
214 line.setLineWidth(lineWidth);
215 line.setPosition(QPointF(0, descriptionheight));
216 descriptionheight += line.height();
217 }
218 layout.endLayout();
219 }
220 return qCeil(descriptionheight);
221}
222
223/*!
224 \reimp
225 */
226QSize QCommandLinkButton::minimumSizeHint() const
227{
228 Q_D(const QCommandLinkButton);
229 QSize size = sizeHint();
230 int minimumHeight = qMax(d->descriptionOffset() + d->bottomMargin(),
231 icon().actualSize(iconSize()).height() + d->topMargin());
232 size.setHeight(minimumHeight);
233 return size;
234}
235
236void QCommandLinkButton::initStyleOption(QStyleOptionButton *option) const
237{
238 QPushButton::initStyleOption(option);
239 option->features |= QStyleOptionButton::CommandLinkButton;
240}
241
242/*!
243 Constructs a command link with no text and a \a parent.
244*/
245
246QCommandLinkButton::QCommandLinkButton(QWidget *parent)
247: QPushButton(*new QCommandLinkButtonPrivate, parent)
248{
249 Q_D(QCommandLinkButton);
250 d->init();
251}
252
253/*!
254 Constructs a command link with the parent \a parent and the text \a
255 text.
256*/
257
258QCommandLinkButton::QCommandLinkButton(const QString &text, QWidget *parent)
259 : QCommandLinkButton(parent)
260{
261 setText(text);
262}
263
264/*!
265 Constructs a command link with a \a text, a \a description, and a \a parent.
266*/
267QCommandLinkButton::QCommandLinkButton(const QString &text, const QString &description, QWidget *parent)
268 : QCommandLinkButton(text, parent)
269{
270 setDescription(description);
271}
272
273/*!
274 Destructor.
275*/
276QCommandLinkButton::~QCommandLinkButton()
277{
278}
279
280/*! \reimp */
281bool QCommandLinkButton::event(QEvent *e)
282{
283 if (e->type() == QEvent::StyleChange) {
284 // If the new style is a QStyleSheetStyle, don't reset the icon, because:
285 // - either it has been explicitly set, in which case we want to keep it.
286 // - or it has been initialised by the previous style, which is now the base style,
287 // in which case we want to keep it as well.
288 // - or it has been set in the style sheet, in which case we don't want to override it here.
289 // When a style sheet with an icon is replaced by one without an icon, the old icon
290 // will be reset, when baseStyle()->repolish() is called.
291#if QT_CONFIG(style_stylesheet)
292 const bool isStyleSheetStyle = qobject_cast<QStyleSheetStyle *>(style());
293#else
294 const bool isStyleSheetStyle = false;
295#endif
296 if (!isStyleSheetStyle) {
297 QStyleOptionButton opt;
298 initStyleOption(&opt);
299 setIcon(style()->standardIcon(QStyle::SP_CommandLink, &opt, this));
300 }
301 }
302
303 return QPushButton::event(e);
304}
305
306/*! \reimp */
307QSize QCommandLinkButton::sizeHint() const
308{
309// Standard size hints from UI specs
310// Without note: 135, 41
311// With note: 135, 60
312 Q_D(const QCommandLinkButton);
313
314 QSize size = QPushButton::sizeHint();
315 QFontMetrics fm(d->titleFont());
316 int textWidth = qMax(fm.horizontalAdvance(text()), 135);
317 int buttonWidth = textWidth + d->textOffset() + d->rightMargin();
318 int heightWithoutDescription = d->descriptionOffset() + d->bottomMargin();
319
320 size.setWidth(qMax(size.width(), buttonWidth));
321 size.setHeight(qMax(d->description.isEmpty() ? 41 : 60,
322 heightWithoutDescription + d->descriptionHeight(buttonWidth)));
323 return size;
324}
325
326/*! \reimp */
327int QCommandLinkButton::heightForWidth(int width) const
328{
329 Q_D(const QCommandLinkButton);
330 int heightWithoutDescription = d->descriptionOffset() + d->bottomMargin();
331 // find the width available for the description area
332 return qMax(heightWithoutDescription + d->descriptionHeight(width),
333 icon().actualSize(iconSize()).height() + d->topMargin() +
334 d->bottomMargin());
335}
336
337/*! \reimp */
338void QCommandLinkButton::paintEvent(QPaintEvent *)
339{
340 Q_D(QCommandLinkButton);
341 QStylePainter p(this);
342
343 QStyleOptionButton option;
344 initStyleOption(&option);
345
346 option.text = QString();
347 option.icon = QIcon(); //we draw this ourselves
348
349 const int vOffset = isDown()
350 ? style()->pixelMetric(QStyle::PM_ButtonShiftVertical, &option, this) : 0;
351 const int hOffset = isDown()
352 ? style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal, &option, this) : 0;
353
354 //Draw icon
355 p.drawControl(QStyle::CE_PushButton, option);
356 if (!icon().isNull()) {
357 const auto size = icon().actualSize(iconSize());
358 const auto mode = isEnabled() ? QIcon::Normal : QIcon::Disabled;
359 const auto state = isChecked() ? QIcon::On : QIcon::Off;
360 const auto rect = QRect(d->leftMargin() + hOffset, d->topMargin() + vOffset,
361 size.width(), size.height());
362 icon().paint(&p, rect, Qt::AlignCenter, mode, state);
363 }
364
365 //Draw title
366 QColor textColor = palette().buttonText().color();
367 if (isEnabled() && d->usingVistaStyle()) {
368 textColor = option.palette.buttonText().color();
369 if (underMouse() && !isDown())
370 textColor = option.palette.brightText().color();
371 //A simple text color transition
372 d->currentColor = d->mergedColors(textColor, d->currentColor, 60);
373 option.palette.setColor(QPalette::ButtonText, d->currentColor);
374 }
375
376 int textflags = Qt::TextShowMnemonic;
377 if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &option, this))
378 textflags |= Qt::TextHideMnemonic;
379
380 p.setFont(d->titleFont());
381 p.drawItemText(d->titleRect().translated(hOffset, vOffset),
382 textflags, option.palette, isEnabled(), text(), QPalette::ButtonText);
383
384 //Draw description
385 textflags |= Qt::TextWordWrap | Qt::ElideRight;
386 p.setFont(d->descriptionFont());
387 p.drawItemText(d->descriptionRect().translated(hOffset, vOffset), textflags,
388 option.palette, isEnabled(), description(), QPalette::ButtonText);
389}
390
391void QCommandLinkButton::setDescription(const QString &description)
392{
393 Q_D(QCommandLinkButton);
394 d->description = description;
395 updateGeometry();
396 update();
397}
398
399QString QCommandLinkButton::description() const
400{
401 Q_D(const QCommandLinkButton);
402 return d->description;
403}
404
405QT_END_NAMESPACE
406
407#include "moc_qcommandlinkbutton.cpp"
int descriptionHeight(int width) const
QColor mergedColors(const QColor &a, const QColor &b, int value) const
\variable QStyleOptionHeaderV2::textElideMode
\reentrant
Definition qtextlayout.h:70
Combined button and popup list for selecting options.