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