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

source code of qtbase/src/widgets/styles/qpixmapstyle.cpp