1/***************************************************************************
2**
3** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
4** Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
5** Copyright (C) 2016 The Qt Company Ltd.
6** Contact: https://www.qt.io/licensing/
7**
8** This file is part of the QtWidgets module of the Qt Toolkit.
9**
10** $QT_BEGIN_LICENSE:LGPL$
11** Commercial License Usage
12** Licensees holding valid commercial Qt licenses may use this file in
13** accordance with the commercial license agreement provided with the
14** Software or, alternatively, in accordance with the terms contained in
15** a written agreement between you and The Qt Company. For licensing terms
16** and conditions see https://www.qt.io/terms-conditions. For further
17** information use the contact form at https://www.qt.io/contact-us.
18**
19** GNU Lesser General Public License Usage
20** Alternatively, this file may be used under the terms of the GNU Lesser
21** General Public License version 3 as published by the Free Software
22** Foundation and appearing in the file LICENSE.LGPL3 included in the
23** packaging of this file. Please review the following information to
24** ensure the GNU Lesser General Public License version 3 requirements
25** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
26**
27** GNU General Public License Usage
28** Alternatively, this file may be used under the terms of the GNU
29** General Public License version 2.0 or (at your option) the GNU General
30** Public license version 3 or any later version approved by the KDE Free
31** Qt Foundation. The licenses are as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
33** included in the packaging of this file. Please review the following
34** information to ensure the GNU General Public License requirements will
35** be met: https://www.gnu.org/licenses/gpl-2.0.html and
36** https://www.gnu.org/licenses/gpl-3.0.html.
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qpixmapstyle_p.h"
43#include "qpixmapstyle_p_p.h"
44
45#include <QDebug>
46#if QT_CONFIG(textedit)
47#include <QTextEdit>
48#endif
49#include <QStringBuilder>
50#include <QPainter>
51#include <QPixmapCache>
52#include <QStyleOption>
53#include <QString>
54#if QT_CONFIG(progressbar)
55#include <QProgressBar>
56#endif
57#if QT_CONFIG(slider)
58#include <QSlider>
59#endif
60#include <QEvent>
61#if QT_CONFIG(combobox)
62#include <QComboBox>
63#endif
64#if QT_CONFIG(itemviews)
65#include <QAbstractItemView>
66#include <QStyledItemDelegate>
67#endif
68#if QT_CONFIG(listview)
69#include <QListView>
70#endif
71#include <QAbstractScrollArea>
72#if QT_CONFIG(scrollbar)
73#include <QScrollBar>
74#endif
75#if QT_CONFIG(scroller)
76#include <qscroller.h>
77#endif
78
79QT_BEGIN_NAMESPACE
80
81/*!
82 \class QPixmapStyle
83 \brief The QPixmapStyle class provides mechanism for writing pixmap based styles.
84
85 \since 5.7
86 \ingroup appearance
87 \inmodule QtWidgets
88 \internal
89
90 This is a convenience class that enables the implementation of a widget style using
91 pixmaps, on the same fashion used by the \l{BorderImage} QML type.
92
93 In order to style a QWidget, one simply needs to call QPixmapStyle::addDescriptor()
94 or QPixmapStyle::addPixmap() with the id of the component to be styled, the path of
95 the image to be used, the margins and the tiling rules:
96
97 \snippet styles/qcustompixmapstyle.cpp 0
98
99 \sa QStyle, QCommonStyle
100*/
101
102/*!
103 \internal
104
105 Constructs a QPixmapStyle object.
106*/
107QPixmapStyle::QPixmapStyle()
108 : QCommonStyle(*new QPixmapStylePrivate)
109{
110}
111
112/*!
113 Destroys the QPixmapStyle object.
114*/
115QPixmapStyle::~QPixmapStyle()
116{
117}
118
119/*!
120 \reimp
121*/
122void QPixmapStyle::polish(QApplication *application)
123{
124 QCommonStyle::polish(application);
125#if 0 // Used to be included in Qt4 for Q_WS_WIN
126 QApplication::setEffectEnabled(Qt::UI_AnimateCombo, false);
127#endif
128}
129
130/*!
131 \reimp
132*/
133void QPixmapStyle::polish(QPalette &palette)
134{
135 palette = proxy()->standardPalette();
136}
137
138/*!
139 \reimp
140*/
141void QPixmapStyle::polish(QWidget *widget)
142{
143 Q_D(QPixmapStyle);
144
145 // Don't fill the interior of the QTextEdit
146#if QT_CONFIG(textedit)
147 if (qobject_cast<QTextEdit*>(widget)) {
148 QPalette p = widget->palette();
149 p.setBrush(QPalette::Base, Qt::NoBrush);
150 widget->setPalette(p);
151 }
152#endif
153#if QT_CONFIG(progressbar)
154 if (QProgressBar *pb = qobject_cast<QProgressBar*>(widget)) {
155 // Center the text in the progress bar
156 pb->setAlignment(Qt::AlignCenter);
157 // Change the font size if needed, as it's used to compute the minimum size
158 QFont font = pb->font();
159 font.setPixelSize(d->descriptors.value(PB_HBackground).size.height()/2);
160 pb->setFont(font);
161 }
162#endif
163#if QT_CONFIG(slider)
164 if (qobject_cast<QSlider*>(widget))
165 widget->installEventFilter(this);
166#endif
167#if QT_CONFIG(combobox)
168 if (QComboBox *cb = qobject_cast<QComboBox*>(widget)) {
169 widget->installEventFilter(this);
170 // NOTE: This will break if the private API of QComboBox changes drastically
171 // Make sure the popup is created so we can change the frame style
172 QAbstractItemView *list = cb->view();
173 list->setProperty("_pixmap_combobox_list", true);
174 list->setItemDelegate(new QStyledItemDelegate(list));
175 QPalette p = list->palette();
176 p.setBrush(QPalette::Active, QPalette::Base, QBrush(Qt::transparent) );
177 p.setBrush(QPalette::Active, QPalette::AlternateBase, QBrush(Qt::transparent) );
178 p.setBrush(QPalette::Inactive, QPalette::Base, QBrush(Qt::transparent) );
179 p.setBrush(QPalette::Inactive, QPalette::AlternateBase, QBrush(Qt::transparent) );
180 p.setBrush(QPalette::Disabled, QPalette::Base, QBrush(Qt::transparent) );
181 p.setBrush(QPalette::Disabled, QPalette::AlternateBase, QBrush(Qt::transparent) );
182 list->setPalette(p);
183
184 QFrame *frame = qobject_cast<QFrame*>(list->parent());
185 if (frame) {
186 const QPixmapStyleDescriptor &desc = d->descriptors.value(DD_PopupDown);
187 const QPixmapStylePixmap &pix = d->pixmaps.value(DD_ItemSeparator);
188 frame->setContentsMargins(pix.margins.left(), desc.margins.top(),
189 pix.margins.right(), desc.margins.bottom());
190 frame->setAttribute(Qt::WA_TranslucentBackground);
191#if 0 // Used to be included in Qt4 for Q_WS_WIN
192 // FramelessWindowHint is needed on windows to make
193 // WA_TranslucentBackground work properly
194 frame->setWindowFlags(widget->windowFlags() | Qt::FramelessWindowHint);
195#endif
196 }
197 }
198#endif // QT_CONFIG(combobox)
199 if (qstrcmp(widget->metaObject()->className(),"QComboBoxPrivateContainer") == 0)
200 widget->installEventFilter(this);
201
202#if QT_CONFIG(scrollarea)
203 if (QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea*>(widget)) {
204 scrollArea->viewport()->setAutoFillBackground(false);
205#if QT_CONFIG(itemviews)
206 if (QAbstractItemView *view = qobject_cast<QAbstractItemView*>(scrollArea)) {
207 view->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
208 view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
209 }
210#endif
211#if QT_CONFIG(gestures) && QT_CONFIG(scroller)
212 QScroller::grabGesture(scrollArea->viewport(), QScroller::LeftMouseButtonGesture);
213#endif
214 }
215#endif // QT_CONFIG(scrollarea)
216#if QT_CONFIG(scrollbar)
217 if (qobject_cast<QScrollBar*>(widget))
218 widget->setAttribute(Qt::WA_OpaquePaintEvent, false);
219#endif
220#if !QT_CONFIG(progressbar) && !QT_CONFIG(combobox)
221 Q_UNUSED(d);
222#endif
223 QCommonStyle::polish(widget);
224}
225
226/*!
227 \reimp
228*/
229void QPixmapStyle::unpolish(QApplication *application)
230{
231 QCommonStyle::unpolish(application);
232}
233
234/*!
235 \reimp
236*/
237void QPixmapStyle::unpolish(QWidget *widget)
238{
239 if (
240#if QT_CONFIG(slider)
241 qobject_cast<QSlider*>(widget)
242#else
243 false
244#endif
245#if QT_CONFIG(combobox)
246 || qobject_cast<QComboBox*>(widget)
247#endif
248 ) {
249 widget->removeEventFilter(this);
250 }
251
252 if (qstrcmp(widget->metaObject()->className(),"QComboBoxPrivateContainer") == 0)
253 widget->removeEventFilter(this);
254
255#if QT_CONFIG(gestures) && QT_CONFIG(scrollarea) && QT_CONFIG(scroller)
256 if (QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea*>(widget))
257 QScroller::ungrabGesture(scrollArea->viewport());
258#endif
259
260 QCommonStyle::unpolish(widget);
261}
262
263/*!
264 \reimp
265*/
266void QPixmapStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
267 QPainter *painter, const QWidget *widget) const
268{
269 switch (element) {
270 case PE_FrameFocusRect: //disable focus rectangle
271 break;
272 case PE_PanelButtonBevel:
273 case PE_PanelButtonCommand:
274 drawPushButton(option, painter, widget);
275 break;
276 case PE_PanelLineEdit:
277 case PE_FrameLineEdit:
278 drawLineEdit(option, painter, widget);
279 break;
280 case PE_Frame:
281#if QT_CONFIG(textedit)
282 case PE_FrameDefaultButton:
283 if (qobject_cast<const QTextEdit*>(widget))
284 drawTextEdit(option, painter, widget);
285 break;
286#endif
287 case PE_IndicatorCheckBox:
288 drawCheckBox(option, painter, widget);
289 break;
290 case PE_IndicatorRadioButton:
291 drawRadioButton(option, painter, widget);
292 break;
293 case PE_PanelItemViewItem:
294#if QT_CONFIG(listview)
295 if (qobject_cast<const QListView*>(widget))
296 drawPanelItemViewItem(option, painter, widget);
297 else
298#endif
299 QCommonStyle::drawPrimitive(element, option, painter, widget);
300 break;
301 default:
302 QCommonStyle::drawPrimitive(element, option, painter, widget);
303 }
304}
305
306/*!
307 \reimp
308*/
309void QPixmapStyle::drawControl(ControlElement element, const QStyleOption *option,
310 QPainter *painter, const QWidget *widget) const
311{
312 Q_D(const QPixmapStyle);
313
314 switch (element) {
315 case CE_ProgressBarGroove:
316 drawProgressBarBackground(option, painter, widget);
317 break;
318 case CE_ProgressBarLabel:
319 drawProgressBarLabel(option, painter, widget);
320 break;
321 case CE_ProgressBarContents:
322 drawProgressBarFill(option, painter, widget);
323 break;
324 case CE_ShapedFrame:
325 // NOTE: This will break if the private API of QComboBox changes drastically
326 if (qstrcmp(widget->metaObject()->className(),"QComboBoxPrivateContainer") == 0) {
327 const QPixmapStyleDescriptor &desc = d->descriptors.value(DD_PopupDown);
328 const QPixmapStylePixmap &pix = d->pixmaps.value(DD_ItemSeparator);
329 QRect rect = option->rect;
330 rect.adjust(-pix.margins.left(), -desc.margins.top(),
331 pix.margins.right(), desc.margins.bottom());
332 bool up = widget->property("_pixmapstyle_combobox_up").toBool();
333 drawCachedPixmap(up ? DD_PopupUp : DD_PopupDown, rect, painter);
334 }
335 else {
336 QCommonStyle::drawControl(element, option, painter, widget);
337 }
338 break;
339 default:
340 QCommonStyle::drawControl(element, option, painter, widget);
341 }
342}
343
344/*!
345 \reimp
346*/
347void QPixmapStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option,
348 QPainter *painter, const QWidget *widget) const
349{
350 switch (cc) {
351 case CC_Slider:
352 drawSlider(option, painter, widget);
353 break;
354 case CC_ComboBox:
355 drawComboBox(option, painter, widget);
356 break;
357 case CC_ScrollBar:
358 drawScrollBar(option, painter, widget);
359 break;
360 default:
361 QCommonStyle::drawComplexControl(cc, option, painter, widget);
362 }
363}
364
365/*!
366 \reimp
367*/
368QSize QPixmapStyle::sizeFromContents(ContentsType type, const QStyleOption *option,
369 const QSize &contentsSize, const QWidget *widget) const
370{
371 switch (type) {
372 case CT_PushButton:
373 return pushButtonSizeFromContents(option, contentsSize, widget);
374 case CT_LineEdit:
375 return lineEditSizeFromContents(option, contentsSize, widget);
376 case CT_ProgressBar:
377 return progressBarSizeFromContents(option, contentsSize, widget);
378 case CT_Slider:
379 return sliderSizeFromContents(option, contentsSize, widget);
380 case CT_ComboBox:
381 return comboBoxSizeFromContents(option, contentsSize, widget);
382 case CT_ItemViewItem:
383 return itemViewSizeFromContents(option, contentsSize, widget);
384 default: ;
385 }
386
387 return QCommonStyle::sizeFromContents(type, option, contentsSize, widget);
388}
389
390/*!
391 \reimp
392*/
393QRect QPixmapStyle::subElementRect(SubElement element, const QStyleOption *option,
394 const QWidget *widget) const
395{
396 Q_D(const QPixmapStyle);
397
398 switch (element) {
399 case SE_LineEditContents:
400 {
401 QRect rect = QCommonStyle::subElementRect(element, option, widget);
402 const QPixmapStyleDescriptor &desc = d->descriptors.value(LE_Enabled);
403 rect.adjust(desc.margins.left(), desc.margins.top(),
404 -desc.margins.right(), -desc.margins.bottom());
405 rect = visualRect(option->direction, option->rect, rect);
406 return rect;
407 }
408 default: ;
409 }
410
411 return QCommonStyle::subElementRect(element, option, widget);
412}
413
414/*!
415 \reimp
416*/
417QRect QPixmapStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *option,
418 SubControl sc, const QWidget *widget) const
419{
420 switch (cc) {
421 case CC_ComboBox:
422 return comboBoxSubControlRect(option, sc, widget);
423 case CC_ScrollBar:
424 return scrollBarSubControlRect(option, sc, widget);
425 default: ;
426 }
427
428 return QCommonStyle::subControlRect(cc, option, sc, widget);
429}
430
431/*!
432 \reimp
433*/
434int QPixmapStyle::pixelMetric(PixelMetric metric, const QStyleOption *option,
435 const QWidget *widget) const
436{
437 Q_D(const QPixmapStyle);
438
439 switch (metric) {
440 case PM_ButtonShiftHorizontal:
441 case PM_ButtonShiftVertical:
442 return 0;
443 case PM_DefaultFrameWidth:
444#if QT_CONFIG(textedit)
445 if (qobject_cast<const QTextEdit*>(widget)) {
446 const QPixmapStyleDescriptor &desc = d->descriptors.value(LE_Enabled);
447 return qMax(qMax(desc.margins.left(), desc.margins.right()),
448 qMax(desc.margins.top(), desc.margins.bottom()));
449 }
450#endif
451 return 0;
452 case PM_IndicatorWidth:
453 return d->pixmaps.value(CB_Enabled).pixmap.width();
454 case PM_IndicatorHeight:
455 return d->pixmaps.value(CB_Enabled).pixmap.height();
456 case PM_CheckBoxLabelSpacing:
457 {
458 const QPixmapStylePixmap &pix = d->pixmaps.value(CB_Enabled);
459 return qMax(qMax(pix.margins.left(), pix.margins.right()),
460 qMax(pix.margins.top(), pix.margins.bottom()));
461 }
462 case PM_ExclusiveIndicatorWidth:
463 return d->pixmaps.value(RB_Enabled).pixmap.width();
464 case PM_ExclusiveIndicatorHeight:
465 return d->pixmaps.value(RB_Enabled).pixmap.height();
466 case PM_RadioButtonLabelSpacing:
467 {
468 const QPixmapStylePixmap &pix = d->pixmaps.value(RB_Enabled);
469 return qMax(qMax(pix.margins.left(), pix.margins.right()),
470 qMax(pix.margins.top(), pix.margins.bottom()));
471 }
472#if QT_CONFIG(slider)
473 case PM_SliderThickness:
474 if (const QStyleOptionSlider *slider =
475 qstyleoption_cast<const QStyleOptionSlider*>(option)) {
476 const QPixmapStyleDescriptor desc =
477 d->descriptors.value(slider->orientation == Qt::Horizontal
478 ? SG_HEnabled : SG_VEnabled);
479 return slider->orientation == Qt::Horizontal
480 ? desc.size.height() : desc.size.width();
481 }
482 break;
483 case PM_SliderControlThickness:
484 if (const QStyleOptionSlider *slider =
485 qstyleoption_cast<const QStyleOptionSlider*>(option)) {
486 const QPixmapStylePixmap pix =
487 d->pixmaps.value(slider->orientation == Qt::Horizontal
488 ? SH_HEnabled : SH_VEnabled);
489 return slider->orientation == Qt::Horizontal
490 ? pix.pixmap.height() : pix.pixmap.width();
491 }
492 break;
493 case PM_SliderLength:
494 if (const QStyleOptionSlider *slider =
495 qstyleoption_cast<const QStyleOptionSlider*>(option)) {
496 const QPixmapStylePixmap pix =
497 d->pixmaps.value(slider->orientation == Qt::Horizontal
498 ? SH_HEnabled : SH_VEnabled);
499 return slider->orientation == Qt::Horizontal
500 ? pix.pixmap.width() : pix.pixmap.height();
501 }
502 break;
503 case PM_ScrollBarExtent:
504 if (const QStyleOptionSlider *slider =
505 qstyleoption_cast<const QStyleOptionSlider*>(option)) {
506 const QPixmapStyleDescriptor desc =
507 d->descriptors.value(slider->orientation == Qt::Horizontal
508 ? SB_Horizontal : SB_Vertical);
509 return slider->orientation == Qt::Horizontal
510 ? desc.size.height() : desc.size.width();
511 }
512 break;
513#endif // QT_CONFIG(slider)
514 case PM_ScrollBarSliderMin:
515 return 0;
516 default: ;
517 }
518
519 return QCommonStyle::pixelMetric(metric, option, widget);
520}
521
522/*!
523 \reimp
524*/
525int QPixmapStyle::styleHint(StyleHint hint, const QStyleOption *option,
526 const QWidget *widget, QStyleHintReturn *returnData) const
527{
528 switch (hint) {
529 case SH_EtchDisabledText:
530 return false;
531 case SH_ComboBox_Popup:
532 return false;
533 default: ;
534 }
535
536 return QCommonStyle::styleHint(hint, option, widget, returnData);
537}
538
539/*!
540 \reimp
541*/
542QStyle::SubControl QPixmapStyle::hitTestComplexControl(QStyle::ComplexControl control,
543 const QStyleOptionComplex *option,
544 const QPoint &pos,
545 const QWidget *widget) const
546{
547 const SubControl sc = QCommonStyle::hitTestComplexControl(control, option, pos, widget);
548 if (control == CC_ScrollBar) {
549 if (sc == SC_ScrollBarAddLine)
550 return SC_ScrollBarAddPage;
551 else if (sc == SC_ScrollBarSubLine)
552 return SC_ScrollBarSubPage;
553 }
554
555 return sc;
556}
557
558/*!
559 \reimp
560*/
561bool QPixmapStyle::eventFilter(QObject *watched, QEvent *event)
562{
563 Q_D(QPixmapStyle);
564#if QT_CONFIG(slider)
565 if (QSlider *slider = qobject_cast<QSlider*>(watched)) {
566 switch (event->type()) {
567 case QEvent::MouseButtonPress:
568 case QEvent::MouseButtonRelease:
569 case QEvent::MouseMove:
570 slider->update();
571 break;
572 default: ;
573 }
574 }
575#endif // QT_CONFIG(slider)
576#if QT_CONFIG(combobox)
577 if (QComboBox *comboBox = qobject_cast<QComboBox*>(watched)) {
578 switch (event->type()) {
579 case QEvent::MouseButtonPress:
580 event->ignore();
581 comboBox->setProperty("_pixmapstyle_combobox_pressed", true);
582 comboBox->repaint();
583 return true;
584 case QEvent::MouseButtonRelease:
585 comboBox->setProperty("_pixmapstyle_combobox_pressed", false);
586 comboBox->repaint();
587 if ( comboBox->view() ) {
588 if ( comboBox->view()->isVisible() || (!comboBox->isEnabled()))
589 comboBox->hidePopup();
590 else
591 comboBox->showPopup();
592 }
593 break;
594 default: ;
595 }
596 }
597#endif // QT_CONFIG(combobox)
598
599 if (qstrcmp(watched->metaObject()->className(),"QComboBoxPrivateContainer") == 0) {
600 if (event->type() == QEvent::Show) {
601 QWidget *widget = qobject_cast<QWidget*>(watched);
602 int yPopup = widget->geometry().top();
603 int yCombo = widget->parentWidget()->mapToGlobal(QPoint(0, 0)).y();
604 QRect geom = widget->geometry();
605 const QPixmapStyleDescriptor &desc = d->descriptors.value(DD_ButtonEnabled);
606 const bool up = yPopup < yCombo;
607 geom.moveTop(geom.top() + (up ? desc.margins.top() : -desc.margins.bottom()));
608 widget->setGeometry(geom);
609 widget->setProperty("_pixmapstyle_combobox_up", up);
610 widget->parentWidget()->setProperty("_pixmapstyle_combobox_up", up);
611 }
612 }
613
614 return QCommonStyle::eventFilter(watched, event);
615}
616
617/*!
618 \fn void QPixmapStyle::addDescriptor(QPixmapStyle::ControlDescriptor control, const QString &fileName, QMargins margins, QTileRules tileRules)
619
620 Associates the pixmap having the given \a fileName with the given \a control. The \a margins parameter describe the boundaries
621 of the pixmap's top-left, top-right, bottom-left and bottom-right corners, as well as the left, right, top and bottorm segments
622 and the middle. The \a tileRules parameter describes how QPixmapStyle is supposed to handle the scaling of the center of the pixmap.
623
624 Use QPixmapStyle::addPixmap() for controls that are not resizable.
625
626 \snippet styles/qcustompixmapstyle.cpp 1
627
628 \sa addPixmap, copyDescriptor
629
630*/
631void QPixmapStyle::addDescriptor(QPixmapStyle::ControlDescriptor control, const QString &fileName,
632 QMargins margins, QTileRules tileRules)
633{
634 Q_D(QPixmapStyle);
635
636 QPixmapStyleDescriptor desc;
637 QImage image(fileName);
638
639 if (image.isNull())
640 return;
641
642 desc.fileName = fileName;
643 desc.margins = margins;
644 desc.tileRules = tileRules;
645 desc.size = image.size();
646
647 d->descriptors[control] = desc;
648}
649
650/*!
651 \fn void QPixmapStyle::copyDescriptor(QPixmapStyle::ControlDescriptor source, QPixmapStyle::ControlDescriptor dest)
652
653 Copies the data associated with the \a source descriptor to the \a dest descriptor.
654
655 \snippet styles/qcustompixmapstyle.cpp 2
656*/
657
658void QPixmapStyle::copyDescriptor(QPixmapStyle::ControlDescriptor source,
659 QPixmapStyle::ControlDescriptor dest)
660{
661 Q_D(QPixmapStyle);
662 d->descriptors[dest] = d->descriptors.value(source);
663}
664
665/*!
666 \fn void QPixmapStyle::drawCachedPixmap(QPixmapStyle::ControlDescriptor control, const QRect &rect, QPainter *painter) const
667
668 Draws the image associated with the current \a control on the given \a rect using the given \a painter.
669*/
670void QPixmapStyle::drawCachedPixmap(QPixmapStyle::ControlDescriptor control, const QRect &rect,
671 QPainter *p) const
672{
673 Q_D(const QPixmapStyle);
674 auto descriptor = d->descriptors.constFind(control);
675 if (descriptor == d->descriptors.constEnd())
676 return;
677 const QPixmap pix = d->getCachedPixmap(control, descriptor.value(), rect.size());
678 Q_ASSERT(!pix.isNull());
679 p->drawPixmap(rect, pix);
680}
681
682/*!
683 \fn void QPixmapStyle::addPixmap(ControlPixmap control, const QString &fileName, QMargins margins)
684
685 Use this function to style statically sized controls such as check boxes.
686
687 \sa addDescriptor, copyPixmap
688*/
689void QPixmapStyle::addPixmap(ControlPixmap control, const QString &fileName,
690 QMargins margins)
691{
692 Q_D(QPixmapStyle);
693
694 QPixmapStylePixmap pix;
695 QPixmap image(fileName);
696
697 if (image.isNull())
698 return;
699
700 pix.pixmap = image;
701 pix.margins = margins;
702
703 d->pixmaps[control] = pix;
704}
705
706/*
707 \fn void QPixmapStyle::copyPixmap(QPixmapStyle::ControlPixmap source, QPixmapStyle::ControlPixmap dest)
708
709 Copies the data associated with the \a source pixmap to the \a dest pixmap.
710
711 \sa addPixmap, addDescriptor, copyDescriptor
712*/
713void QPixmapStyle::copyPixmap(QPixmapStyle::ControlPixmap source, QPixmapStyle::ControlPixmap dest)
714{
715 Q_D(QPixmapStyle);
716 d->pixmaps[dest] = d->pixmaps.value(source);
717}
718
719/*!
720 \internal
721
722 Constructs a QPixmapStyle object.
723*/
724QPixmapStyle::QPixmapStyle(QPixmapStylePrivate &dd)
725 : QCommonStyle(dd)
726{}
727
728void QPixmapStyle::drawPushButton(const QStyleOption *option,
729 QPainter *painter, const QWidget *) const
730{
731 const bool checked = option->state & State_On;
732 const bool pressed = option->state & State_Sunken;
733 const bool enabled = option->state & State_Enabled;
734
735 ControlDescriptor control = PB_Enabled;
736 if (enabled)
737 control = pressed ? PB_Pressed : (checked ? PB_Checked : PB_Enabled);
738 else
739 control = checked ? PB_PressedDisabled : PB_Disabled;
740 drawCachedPixmap(control, option->rect, painter);
741}
742
743void QPixmapStyle::drawLineEdit(const QStyleOption *option,
744 QPainter *painter, const QWidget *widget) const
745{
746 // Don't draw for the line edit inside a combobox
747#if QT_CONFIG(combobox)
748 if (widget && qobject_cast<const QComboBox*>(widget->parentWidget()))
749 return;
750#else
751 Q_UNUSED(widget);
752#endif
753 const bool enabled = option->state & State_Enabled;
754 const bool focused = option->state & State_HasFocus;
755 ControlDescriptor control = enabled ? (focused ? LE_Focused : LE_Enabled) : LE_Disabled;
756 drawCachedPixmap(control, option->rect, painter);
757}
758
759void QPixmapStyle::drawTextEdit(const QStyleOption *option,
760 QPainter *painter, const QWidget *) const
761{
762 const bool enabled = option->state & State_Enabled;
763 const bool focused = option->state & State_HasFocus;
764 ControlDescriptor control = enabled ? (focused ? TE_Focused : TE_Enabled) : TE_Disabled;
765 drawCachedPixmap(control, option->rect, painter);
766}
767
768void QPixmapStyle::drawCheckBox(const QStyleOption *option,
769 QPainter *painter, const QWidget *) const
770{
771 Q_D(const QPixmapStyle);
772
773 const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton*>(option);
774
775 const bool down = button->state & State_Sunken;
776 const bool enabled = button->state & State_Enabled;
777 const bool on = button->state & State_On;
778
779 ControlPixmap control;
780 if (enabled)
781 control = on ? (down ? CB_PressedChecked : CB_Checked) : (down ? CB_Pressed : CB_Enabled);
782 else
783 control = on ? CB_DisabledChecked : CB_Disabled;
784 painter->drawPixmap(button->rect, d->pixmaps.value(control).pixmap);
785}
786
787void QPixmapStyle::drawRadioButton(const QStyleOption *option,
788 QPainter *painter, const QWidget *) const
789{
790 Q_D(const QPixmapStyle);
791
792 const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton*>(option);
793
794 const bool down = button->state & State_Sunken;
795 const bool enabled = button->state & State_Enabled;
796 const bool on = button->state & State_On;
797
798 ControlPixmap control;
799 if (enabled)
800 control = on ? RB_Checked : (down ? RB_Pressed : RB_Enabled);
801 else
802 control = on ? RB_DisabledChecked : RB_Disabled;
803 painter->drawPixmap(button->rect, d->pixmaps.value(control).pixmap);
804}
805
806void QPixmapStyle::drawPanelItemViewItem(const QStyleOption *option, QPainter *painter,
807 const QWidget *widget) const
808{
809 Q_D(const QPixmapStyle);
810
811 ControlPixmap cp = ID_Separator;
812 ControlDescriptor cd = ID_Selected;
813
814 if (widget && widget->property("_pixmap_combobox_list").toBool()) {
815 cp = DD_ItemSeparator;
816 cd = DD_ItemSelected;
817 }
818
819 QPixmap pix = d->pixmaps.value(cp).pixmap;
820 QRect rect = option->rect;
821 rect.setBottom(rect.top() + pix.height()-1);
822 painter->drawPixmap(rect, pix);
823 if (option->state & QStyle::State_Selected) {
824 rect = option->rect;
825 rect.setTop(rect.top() + pix.height());
826 drawCachedPixmap(cd, rect, painter);
827 }
828}
829
830void QPixmapStyle::drawProgressBarBackground(const QStyleOption *option,
831 QPainter *painter, const QWidget *) const
832{
833 bool vertical = false;
834 if (const QStyleOptionProgressBar *pb =
835 qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
836 vertical = pb->orientation == Qt::Vertical;
837 }
838 drawCachedPixmap(vertical ? PB_VBackground : PB_HBackground, option->rect, painter);
839}
840
841void QPixmapStyle::drawProgressBarLabel(const QStyleOption *option,
842 QPainter *painter, const QWidget *) const
843{
844 if (const QStyleOptionProgressBar *pb =
845 qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
846 const bool vertical = pb->orientation == Qt::Vertical;
847 if (!vertical) {
848 QPalette::ColorRole textRole = QPalette::ButtonText;
849 proxy()->drawItemText(painter, pb->rect,
850 Qt::AlignCenter | Qt::TextSingleLine, pb->palette,
851 pb->state & State_Enabled, pb->text, textRole);
852 }
853 }
854}
855
856void QPixmapStyle::drawProgressBarFill(const QStyleOption *option,
857 QPainter *painter, const QWidget *) const
858{
859 const QStyleOptionProgressBar *pbar =
860 qstyleoption_cast<const QStyleOptionProgressBar*>(option);
861 const bool vertical = pbar->orientation == Qt::Vertical;
862 const bool flip = (pbar->direction == Qt::RightToLeft) ^ pbar->invertedAppearance;
863
864 if (pbar->progress == pbar->maximum) {
865 drawCachedPixmap(vertical ? PB_VComplete : PB_HComplete, option->rect, painter);
866
867 } else {
868 if (pbar->progress == pbar->minimum)
869 return;
870 const auto totalSteps = qint64(pbar->maximum) - pbar->minimum;
871 const auto progressSteps = qint64(pbar->progress) - pbar->minimum;
872 const auto availablePixels = vertical ? option->rect.height() : option->rect.width();
873 const auto pixelsPerStep = double(availablePixels) / totalSteps;
874
875 const auto progress = static_cast<int>(progressSteps * pixelsPerStep); // width in pixels
876
877 QRect optRect = option->rect;
878 if (vertical) {
879 if (flip)
880 optRect.setBottom(optRect.top()+progress-1);
881 else
882 optRect.setTop(optRect.bottom()-progress+1);
883 } else {
884 if (flip)
885 optRect.setLeft(optRect.right()-progress+1);
886 else
887 optRect.setRight(optRect.left()+progress-1);
888 }
889
890 drawCachedPixmap(vertical ? PB_VContent : PB_HContent, optRect, painter);
891 }
892}
893
894void QPixmapStyle::drawSlider(const QStyleOptionComplex *option,
895 QPainter *painter, const QWidget *widget) const
896{
897#if QT_CONFIG(slider)
898 Q_D(const QPixmapStyle);
899
900 const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider*>(option);
901 if (!slider)
902 return;
903
904 const bool enabled = option->state & State_Enabled;
905 const bool pressed = option->state & State_Sunken;
906 const Qt::Orientation orient = slider->orientation;
907
908 const QRect handle = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget);
909 if (option->subControls & SC_SliderGroove) {
910 QRect groove = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget);
911 if (groove.isValid()) {
912 // Draw the background
913 ControlDescriptor control;
914 if (orient == Qt::Horizontal)
915 control = enabled ? SG_HEnabled : SG_HDisabled;
916 else
917 control = enabled ? SG_VEnabled : SG_VDisabled;
918 drawCachedPixmap(control, groove, painter);
919
920 // Draw the active part
921 if (orient == Qt::Horizontal) {
922 control = enabled ? (pressed ? SG_HActivePressed : SG_HActiveEnabled )
923 : SG_HActiveDisabled;
924 } else {
925 control = enabled ? (pressed ? SG_VActivePressed : SG_VActiveEnabled )
926 : SG_VActiveDisabled;
927 }
928 const QPixmapStyleDescriptor &desc = d->descriptors.value(control);
929 const QPixmap pix = d->getCachedPixmap(control, desc, groove.size());
930 if (!pix.isNull()) {
931 groove.setRight(orient == Qt::Horizontal
932 ? handle.center().x() : handle.center().y());
933 painter->drawPixmap(groove, pix, groove);
934 }
935 }
936 }
937 if (option->subControls & SC_SliderHandle) {
938 if (handle.isValid()) {
939 ControlPixmap pix;
940 if (orient == Qt::Horizontal)
941 pix = enabled ? (pressed ? SH_HPressed : SH_HEnabled) : SH_HDisabled;
942 else
943 pix = enabled ? (pressed ? SH_VPressed : SH_VEnabled) : SH_VDisabled;
944 painter->drawPixmap(handle, d->pixmaps.value(pix).pixmap);
945 }
946 }
947#else
948 Q_UNUSED(option);
949 Q_UNUSED(painter);
950 Q_UNUSED(widget);
951#endif // QT_CONFIG(slider)
952}
953
954void QPixmapStyle::drawComboBox(const QStyleOptionComplex *option,
955 QPainter *painter, const QWidget *widget) const
956{
957 Q_D(const QPixmapStyle);
958
959 const bool enabled = option->state & State_Enabled;
960 const bool pressed = widget->property("_pixmapstyle_combobox_pressed").toBool();
961 const bool opened = option->state & State_On;
962
963 ControlDescriptor control =
964 enabled ? (pressed ? DD_ButtonPressed : DD_ButtonEnabled) : DD_ButtonDisabled;
965 drawCachedPixmap(control, option->rect, painter);
966
967 ControlPixmap cp = enabled ? (opened ? DD_ArrowOpen
968 : (pressed ? DD_ArrowPressed : DD_ArrowEnabled))
969 : DD_ArrowDisabled;
970 QPixmapStylePixmap pix = d->pixmaps.value(cp);
971 QRect rect = comboBoxSubControlRect(option, SC_ComboBoxArrow, widget);
972 painter->drawPixmap(rect, pix.pixmap);
973}
974
975void QPixmapStyle::drawScrollBar(const QStyleOptionComplex *option,
976 QPainter *painter, const QWidget *widget) const
977{
978#if QT_CONFIG(slider)
979 if (const QStyleOptionSlider *slider =
980 qstyleoption_cast<const QStyleOptionSlider*>(option)) {
981 // Do not draw the scrollbar
982 if (slider->minimum == slider->maximum)
983 return;
984
985 QRect rect = scrollBarSubControlRect(option, SC_ScrollBarSlider, widget);
986 ControlDescriptor control = slider->orientation == Qt::Horizontal
987 ? SB_Horizontal : SB_Vertical;
988 drawCachedPixmap(control, rect, painter);
989 }
990#else
991 Q_UNUSED(option);
992 Q_UNUSED(painter);
993 Q_UNUSED(widget);
994#endif // QT_CONFIG(slider)
995}
996
997QSize QPixmapStyle::pushButtonSizeFromContents(const QStyleOption *option,
998 const QSize &contentsSize,
999 const QWidget *widget) const
1000{
1001 Q_D(const QPixmapStyle);
1002
1003 const QPixmapStyleDescriptor &desc = d->descriptors.value(PB_Enabled);
1004 const int bm = proxy()->pixelMetric(PM_ButtonMargin, option, widget);
1005
1006 int w = contentsSize.width();
1007 int h = contentsSize.height();
1008 w += desc.margins.left() + desc.margins.right() + bm;
1009 h += desc.margins.top() + desc.margins.bottom() + bm;
1010
1011 return d->computeSize(desc, w, h);
1012}
1013
1014QSize QPixmapStyle::lineEditSizeFromContents(const QStyleOption *,
1015 const QSize &contentsSize, const QWidget *) const
1016{
1017 Q_D(const QPixmapStyle);
1018
1019 const QPixmapStyleDescriptor &desc = d->descriptors.value(LE_Enabled);
1020 const int border = 2 * proxy()->pixelMetric(PM_DefaultFrameWidth);
1021
1022 int w = contentsSize.width() + border + desc.margins.left() + desc.margins.right();
1023 int h = contentsSize.height() + border + desc.margins.top() + desc.margins.bottom();
1024
1025 return d->computeSize(desc, w, h);
1026}
1027
1028QSize QPixmapStyle::progressBarSizeFromContents(const QStyleOption *option,
1029 const QSize &contentsSize,
1030 const QWidget *widget) const
1031{
1032 Q_D(const QPixmapStyle);
1033
1034 bool vertical = false;
1035 if (const QStyleOptionProgressBar *pb =
1036 qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
1037 vertical = pb->orientation == Qt::Vertical;
1038 }
1039 QSize result = QCommonStyle::sizeFromContents(CT_Slider, option, contentsSize, widget);
1040 if (vertical) {
1041 const QPixmapStyleDescriptor desc = d->descriptors.value(PB_VBackground);
1042 return QSize(desc.size.height(), result.height());
1043 } else {
1044 const QPixmapStyleDescriptor desc = d->descriptors.value(PB_HBackground);
1045 return QSize(result.width(), desc.size.height());
1046 }
1047}
1048
1049QSize QPixmapStyle::sliderSizeFromContents(const QStyleOption *option,
1050 const QSize &contentsSize,
1051 const QWidget *widget) const
1052{
1053#if QT_CONFIG(slider)
1054 Q_D(const QPixmapStyle);
1055
1056 const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider*>(option);
1057 if (!slider)
1058 return QSize();
1059
1060 QSize result = QCommonStyle::sizeFromContents(CT_Slider, option, contentsSize, widget);
1061
1062 const QPixmapStyleDescriptor desc = d->descriptors.value(slider->orientation == Qt::Horizontal
1063 ? SG_HEnabled : SG_VEnabled);
1064
1065 if (slider->orientation == Qt::Horizontal)
1066 return QSize(result.width(), desc.size.height());
1067 else
1068 return QSize(desc.size.width(), result.height());
1069#else // QT_CONFIG(slider)
1070 Q_UNUSED(option);
1071 Q_UNUSED(contentsSize);
1072 Q_UNUSED(widget);
1073 return QSize();
1074#endif // QT_CONFIG(slider)
1075}
1076
1077QSize QPixmapStyle::comboBoxSizeFromContents(const QStyleOption *option,
1078 const QSize &contentsSize,
1079 const QWidget *widget) const
1080{
1081 Q_D(const QPixmapStyle);
1082
1083 const QPixmapStyleDescriptor &desc = d->descriptors.value(DD_ButtonEnabled);
1084
1085 QSize result = QCommonStyle::sizeFromContents(CT_ComboBox, option, contentsSize, widget);
1086 return d->computeSize(desc, result.width(), result.height());
1087}
1088
1089QSize QPixmapStyle::itemViewSizeFromContents(const QStyleOption *option,
1090 const QSize &contentsSize,
1091 const QWidget *widget) const
1092{
1093 Q_D(const QPixmapStyle);
1094
1095 QSize size = QCommonStyle::sizeFromContents(CT_ItemViewItem, option, contentsSize, widget);
1096
1097 ControlPixmap cp = ID_Separator;
1098 ControlDescriptor cd = ID_Selected;
1099 if (widget && widget->property("_pixmap_combobox_list").toBool()) {
1100 cp = DD_ItemSeparator;
1101 cd = DD_ItemSelected;
1102 }
1103
1104 const QPixmapStyleDescriptor &desc = d->descriptors.value(cd);
1105 const QPixmapStylePixmap &pix = d->pixmaps.value(cp);
1106 size.setHeight(qMax(size.height(),
1107 desc.size.height() + pix.pixmap.height()));
1108 return size;
1109}
1110
1111QRect QPixmapStyle::comboBoxSubControlRect(const QStyleOptionComplex *option,
1112 QStyle::SubControl sc, const QWidget *) const
1113{
1114 Q_D(const QPixmapStyle);
1115
1116 QRect r = option->rect; // Default size
1117 const QPixmapStylePixmap &pix = d->pixmaps.value(DD_ArrowEnabled);
1118 const QPixmapStyleDescriptor &desc = d->descriptors.value(DD_ButtonEnabled);
1119
1120 switch (sc) {
1121 case SC_ComboBoxArrow:
1122 r.setRect(r.right() - pix.margins.right() - pix.pixmap.width(),
1123 r.top() + pix.margins.top(),
1124 pix.pixmap.width(), pix.pixmap.height());
1125 break;
1126 case SC_ComboBoxEditField:
1127 r.adjust(desc.margins.left(), desc.margins.right(),
1128 -desc.margins.right(), -desc.margins.bottom());
1129 r.setRight(r.right() - pix.margins.right() - pix.margins.left() - pix.pixmap.width());
1130 break;
1131 default:
1132 break;
1133 }
1134
1135 r = visualRect(option->direction, option->rect, r);
1136 return r;
1137}
1138
1139QRect QPixmapStyle::scrollBarSubControlRect(const QStyleOptionComplex *option,
1140 QStyle::SubControl sc, const QWidget *) const
1141{
1142#if QT_CONFIG(slider)
1143 if (const QStyleOptionSlider *slider =
1144 qstyleoption_cast<const QStyleOptionSlider*>(option)) {
1145 int length = (slider->orientation == Qt::Horizontal)
1146 ? slider->rect.width() : slider->rect.height();
1147 int page = length * slider->pageStep
1148 / (slider->maximum - slider->minimum + slider->pageStep);
1149 int pos = length * slider->sliderValue
1150 / (slider->maximum - slider->minimum + slider->pageStep);
1151 pos = qMin(pos+page, length) - page;
1152
1153 QRect rect = slider->rect;
1154
1155 if (slider->orientation == Qt::Horizontal) {
1156 switch (sc) {
1157 case SC_ScrollBarAddPage:
1158 rect.setLeft(pos+page);
1159 return rect;
1160 case SC_ScrollBarSubPage:
1161 rect.setRight(pos);
1162 return rect;
1163 case SC_ScrollBarGroove:
1164 return rect;
1165 case SC_ScrollBarSlider:
1166 rect.setLeft(pos);
1167 rect.setRight(pos+page);
1168 return rect;
1169 default: ;
1170 }
1171 } else {
1172 switch (sc) {
1173 case SC_ScrollBarAddPage:
1174 rect.setTop(pos+page);
1175 return rect;
1176 case SC_ScrollBarSubPage:
1177 rect.setBottom(pos);
1178 return rect;
1179 case SC_ScrollBarGroove:
1180 return rect;
1181 case SC_ScrollBarSlider:
1182 rect.setTop(pos);
1183 rect.setBottom(pos+page);
1184 return rect;
1185 default: ;
1186 }
1187 }
1188 }
1189#else
1190 Q_UNUSED(option);
1191 Q_UNUSED(sc);
1192#endif // QT_CONFIG(slider)
1193 return QRect();
1194}
1195
1196QPixmap QPixmapStylePrivate::scale(int w, int h, const QPixmap &pixmap, const QPixmapStyleDescriptor &desc)
1197{
1198 QPixmap result(w, h);
1199 {
1200 const QColor transparent(0, 0, 0, 0);
1201 result.fill( transparent );
1202 QPainter p( &result );
1203 const QMargins margins = desc.margins;
1204 qDrawBorderPixmap(&p, result.rect(), margins, pixmap,
1205 pixmap.rect(), margins, desc.tileRules);
1206 }
1207 return result;
1208}
1209
1210QPixmap QPixmapStylePrivate::getCachedPixmap(QPixmapStyle::ControlDescriptor control,
1211 const QPixmapStyleDescriptor &desc,
1212 const QSize &size) const
1213{
1214 Q_Q(const QPixmapStyle);
1215
1216 const QString sizeString = QString::number(size.width()) % QLatin1Char('*')
1217 % QString::number(size.height());
1218 const QString key = QLatin1String(q->metaObject()->className()) % QString::number(control)
1219 % QLatin1Char('@') % sizeString;
1220
1221 QPixmap result;
1222
1223 if (!QPixmapCache::find( key, &result)) {
1224 QPixmap source(desc.fileName);
1225 result = scale(size.width(), size.height(), source, desc);
1226 QPixmapCache::insert(key, result);
1227 }
1228 return result;
1229}
1230
1231QSize QPixmapStylePrivate::computeSize(const QPixmapStyleDescriptor &desc, int width, int height) const
1232{
1233 if (desc.tileRules.horizontal != Qt::RepeatTile)
1234 width = qMax(width, desc.size.width());
1235 if (desc.tileRules.vertical != Qt::RepeatTile)
1236 height = qMax(height, desc.size.height());
1237 return QSize(width, height);
1238}
1239
1240QT_END_NAMESPACE
1241